数据结构 - 排序 | C (插入、希尔、选择、堆、冒泡)

目录

  • 0.排序
  • 思维导图(总)
  • 一、插入排序
    • 1.直接插入排序
        • 思路分析
        • 代码实现
        • 时间复杂度
    • 2.希尔排序
        • 思路分析
        • 代码实现
        • 时间复杂度
  • 二、选择排序
    • 1.选择排序
        • 思路分析
        • 代码实现
        • 时间复杂度
    • 2.堆排序
        • 思路分析
        • 代码实现
        • 时间复杂度
  • 三、交换排序
    • 冒泡排序
        • 思路分析
        • 代码实现
        • 时间复杂度

0.排序

// Sort.h
#pragma once

#include 
#include 
#include 
#include 
#include "Stack.h"

// 插入排序
void InsertSort(int* a, int n);

// 希尔排序
void ShellSort(int* a, int n);

// 选择排序
void SelectSort(int* a, int n);


// 堆排序
void AdjustDwon(int* a, int n, int root);
void HeapSort(int* a, int n);

// 冒泡排序
void BubbleSort(int* a, int n);

思维导图(总)

数据结构 - 排序 | C (插入、希尔、选择、堆、冒泡)_第1张图片

一、插入排序

1.直接插入排序

思路分析

  • 升序排序

  • 一个数插入一个升序列,找到不比自己大的数,插入在这个数的后面
    数据结构 - 排序 | C (插入、希尔、选择、堆、冒泡)_第2张图片

  • 具体是如何插入的:

    • 如下图,如果 a[end]>tmp 就往后挪动数据→a[end+1]=a[end]当找到 不比tmp大的数 之后,将tmp插入到这个数的后面→a[end+1]=tmp
      数据结构 - 排序 | C (插入、希尔、选择、堆、冒泡)_第3张图片
  • 因此,对于一个无序列,就是依次插入数据
    数据结构 - 排序 | C (插入、希尔、选择、堆、冒泡)_第4张图片

代码实现

// 插入排序
void InsertSort(int* a, int n)
{
	assert(a);
	for (int i = 1; i < n; i++)
	{
		int tmp = a[i];
		int end = i - 1;
		while (end >= 0)
		{
			if (a[end] <= tmp)
			{
				break;
			}
			a[end + 1] = a[end--];
		}
		a[end + 1] = tmp;
	}
}

时间复杂度

  • 最坏的情况:排升序,但原数列为降序
    array[0]挪动数据并插入的次数为:0;
    array[1]挪动数据并插入的次数为:1;
    array[2]挪动数据并插入的次数为:2;
    array[3]挪动数据并插入的次数为:3;
    …………
    array[n]挪动数据并插入的次数为:n;
    Σ = 0+1+2+3+……+n = (1+n)*n/2

时间复杂度O(N)=N²

  • 最好的情况:时间复杂度O(N)=N

插入排序的适应性很强,对于有序、局部有序的情况,都能效率提升

2.希尔排序

思路分析

为了优化直接插入,我们选择进行预排序,让较大的位于前列的数先排到较后面的位置,以此减少挪动数据的次数
数据结构 - 排序 | C (插入、希尔、选择、堆、冒泡)_第5张图片
预排序:分组,每组进行插入排序

下图以gap==3为例
数据结构 - 排序 | C (插入、希尔、选择、堆、冒泡)_第6张图片
数据结构 - 排序 | C (插入、希尔、选择、堆、冒泡)_第7张图片
gap的变化:

while(gap)
{
	int gap = n / 2;
	gap /= 2;
}
//当gap==1时就是直接插入排序
//or
while(gap)
{
	int gap = n / 3;
	gap = (gap/3 + 1);
}

代码实现

// 希尔排序
void ShellSort(int* a, int n)
{
	assert(a);

	int gap = n / 2;
	while (gap)
	{
		for (int i = 1; i < n; i++)
		{
			int tmp = a[i];
			int end = i - gap;
			while (end >= 0)
			{
				if (a[end] <= tmp)
				{
					break;
				}
				a[end + gap] = a[end];
				end -= gap;
			}
			a[end + gap] = tmp;
		}
		gap /= 2;
	}
}

时间复杂度

希尔排序的时间复杂度算起来很复杂,预排序之后无法确定数列的顺序情况。
这里直接给结果:O(N)=N^1.3

二、选择排序

1.选择排序

思路分析

遍历数组(以排升序为例)
找到最一小的数与数组第一个位置的数交换;
找到第二小的数与数组第二个位置的数交换;
……(依次类推)
数据结构 - 排序 | C (插入、希尔、选择、堆、冒泡)_第8张图片

优化:两头缩进
从数组的头和尾开始,在中间同时找最大和最小的数,找到分别与“两头的数”交换,依次类推,“两头缩进”
数据结构 - 排序 | C (插入、希尔、选择、堆、冒泡)_第9张图片

代码实现

//优化后:
void Swap(int* x, int* y)
{
	assert(x && y);
	int tmp = *x;
	*x = *y;
	*y = tmp;
}

// 选择排序
void SelectSort(int* a, int n)
{
	assert(a);
	//i < (n - 1) / 2  即 begin < end 即 i
	for (int i = 0; i < (n - 1) / 2; i++)
	{
		int begin = i, end = n - 1 - i;
		int mini = begin, maxi = end;

		//[begin+1,end-1]在这个区间内找到最大和最小的数
		for (int j = begin + 1; j <= end - 1; j++)
		{
			if (a[j] < a[mini])
				mini = j;
			if (a[j] > a[maxi])
				maxi = j;
		}
		//min = a[mini], max = a[maxi] 找到之后交换
		Swap(&a[begin], &a[mini]);
		Swap(&a[end], &a[maxi]);
	}
}

时间复杂度

未优化的算法:
从头找到尾
找到最一小的数与数组第一个位置的数交换,查找的次数为:n - 1;
找到第二小的数与数组第二个位置的数交换,查找的次数为:n - 2;
…………
最后一次查找次数为:1( n -(n-1))
Σ = 1+2+3+……+(n-1)= n*(n-1)/2

时间复杂度为O(N²)

2.堆排序

详细分析过程见→二叉树-堆| [堆的实现【建堆算法图解+分析】]

思路分析

排升序:建大堆。把数组按大堆的方式排列,此时堆顶就是最大的数,让堆顶与数组最后一个数交换,最大的数就被放在了数组的尾部。对于前面的部分,调整使其依旧为大堆,此时堆顶的数就是第二大的数……重复这个操作向下调整建堆,不断取堆顶的数
排降序:建小堆。思路同上。

代码实现

// 堆排序
void AdjustDwon(int* a, int n, int root)
{
	assert(a);
	int parent = root;
	int child = parent * 2 + 1;

	while (child < n)
	{
		if ((child + 1) < n && a[child + 1] > a[child])
			++child;
		if (a[parent] < a[child])
			Swap(&a[parent], &a[child]);
		else
			break;

		parent = child;
		child = parent * 2 + 1;
	}
}
void HeapSort(int* a, int n)
{
	assert(a);

	//建大堆
	for (int parent = (n - 1 - 1) / 2; parent >= 0; parent--)
	{
		AdjustDwon(a, n, parent);
	}

	//取堆顶的数据并向下调整
	int end = n - 1;
	while (end)
	{
		Swap(&a[0], &a[end]);
		AdjustDwon(a, end, 0);
		end--;
	}
}

时间复杂度

向下调整建堆的时间复杂度:O(N)

取堆顶数据然后向下调整时间复杂度:O(logN) → 重复 N次 → O(N*logN)

时间复杂度 为 O(N*logN)

三、交换排序

冒泡排序

思路分析

前后比较交换

以下列这个数组为例,展示一趟冒泡排序:
在这里插入图片描述
在这里插入图片描述

代码实现

// 冒泡排序
void BubbleSort(int* a, int n)
{
	assert(a);
	for (int j = 0; j < n; j++)
	{
		for (int i = 0; i < n - j - 1; i++)
		{
			if (a[i] > a[i + 1])
				Swap(&a[i], &a[i + 1]);
		}
	}
}

时间复杂度

第一趟冒泡排序:n-1次
第二趟冒泡排序:n-2次
……
第n趟冒泡排序:n-n次
Σ = 1 + 2 + 3 + …… + n-1 = n(n-1)/2

时间复杂度为O(N)=N²

你可能感兴趣的:(数据结构初阶,数据结构,c语言,算法)