C/C++排序算法(一) —— 插入排序和希尔排序

文章目录

  • 前言
  • 1. 直接插入排序
    • 基本思想
    • 具体步骤
    • 动图演示
    • 代码实现
    • 复杂度分析
  • 2. 希尔排序
    • 基本思想
    • 具体步骤
    • 动图演示
    • 代码实现
    • 复杂度分析
  • 3. 总结


前言

今天我们将学习排序算法中的 直接插入排序希尔排序,因为希尔排序的思想本质就是在插入,所以这两个可以统称为 插入排序

1. 直接插入排序

基本思想

相信大家都玩过扑克牌吧,那么如何进行扑克牌的排序呢?

举个例子,比如我手中有红桃 6,7,9,10 这 4 张牌,已经处于升序排列:
C/C++排序算法(一) —— 插入排序和希尔排序_第1张图片

时候,我又抓到一张红桃 8,如何让手中的 5 张牌重新变成升序呢?
C/C++排序算法(一) —— 插入排序和希尔排序_第2张图片

最简单的方式,就是在已经有序的 4 张牌中找到红桃 8 应该插入的位置,也就是 7 和 9 之间,把红桃 8 插进去:
C/C++排序算法(一) —— 插入排序和希尔排序_第3张图片

就像玩牌一样,有一种排序算法也采用了类似的思想:维护一个有序区,把元素一个个插入有序区的适当位置,直到所有元素都有序为止。

这样的排序算法,被称为直接插入排序。

具体步骤

直接插入排序是一种简单的插入排序法,其基本思想是:

  • 把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。
  • 在待排序的元素中,假设前 n-1 个元素已有序,现将第 n 个元素插入到前面已经排好的序列中,使得前 n 个元素有序。按照此法对所有元素进行插入,直到整个序列有序。

接下来演示一下直接插入排序在数组中的具体实现步骤。

给定一组无序数组如下:
C/C++排序算法(一) —— 插入排序和希尔排序_第4张图片

我们把首元素 6 作为有序区,此时有序区只有这一个元素:
C/C++排序算法(一) —— 插入排序和希尔排序_第5张图片

第一轮,让元素 9 和有序区的元素依次比较,9 > 6,所以元素 9 和元素 6 无需交换。

此时有序区的元素增加到两个:
C/C++排序算法(一) —— 插入排序和希尔排序_第6张图片

第二轮,让元素 7 和有序区的元素依次比较,7 < 9,所以把元素 7 和元素 9 进行交换:
C/C++排序算法(一) —— 插入排序和希尔排序_第7张图片
7 > 6,所以把元素 7 和元素 6 无需交换。

此时有序区的元素增加到三个:
C/C++排序算法(一) —— 插入排序和希尔排序_第8张图片

第三轮,让元素 4 和有序区的元素依次比较,4 < 9,所以把元素 4 和元素 9 进行交换:
C/C++排序算法(一) —— 插入排序和希尔排序_第9张图片
4 < 7,所以把元素 4 和元素 7 进行交换:
C/C++排序算法(一) —— 插入排序和希尔排序_第10张图片
4 < 6,所以把元素 4 和元素 6 进行交换:
C/C++排序算法(一) —— 插入排序和希尔排序_第11张图片
此时有序区的元素增加到四个:
C/C++排序算法(一) —— 插入排序和希尔排序_第12张图片
以此类推,插入排序一共会进行(数组长度-1)轮,每一轮的结果如下:

C/C++排序算法(一) —— 插入排序和希尔排序_第13张图片

动图演示

我们来看一组动图演示

C/C++排序算法(一) —— 插入排序和希尔排序_第14张图片

代码实现

代码示例

void InsertSort(int* a, int n) {
	//数组的长度是n,那么最后一个数据是n-1,倒数第二个数据是n-2
	for (int i = 0; i < n - 1; ++i) {
		// [0 end]有序,把end+1的位置的值插入进去,保持它依旧有序
		int end = i; //记录有序序列的最后一个元素的下标
		int tmp = a[end + 1]; //待插入的元素
		while (end >= 0) {
			if (tmp < a[end]) {
				a[end + 1] = a[end];
				--end;
			}
			else {
				break;
			}
		}
		//代码执行到此位置有两种情况:
		//1.待插入元素找到应插入位置(break跳出循环到此)。
		//2.待插入元素比当前有序序列中的所有元素都小(while循环结束后到此)。
		a[end + 1] = tmp;
	}
}

复杂度分析

时间复杂度: O ( N 2 ) O(N^2) O(N2)

  • 最好的情况:数组是有序的或者接近有序的,那么时间复杂度就是: O ( N ) O(N) O(N)
  • 最坏的情况:数组是逆序的,那么时间复杂度就是: O ( N 2 ) O(N^2) O(N2)
  • 元素集合越接近有序,直接插入排序算法的时间效率越高。

空间复杂度: O ( 1 ) O(1) O(1)

  • 这里没有额外开辟空间

2. 希尔排序

基本思想

希尔排序(Shell Sort)是插入排序的一种,它是针对直接插入排序算法的改进。

它通过比较相距一定间隔的元素来进行,各趟比较所用的距离随着算法的进行而减小,直到只比较相邻元素的最后一趟排序为止。所以又被称为缩小增量排序。

具体步骤

基本思想是:

  • 先选定一个小于 N 的整数 gap 作为第一增量,然后将所有距离为 gap 的元素分在同一组,并对每一组的元素进行直接插入排序。然后再取一个比第一增量小的整数作为第二增量,重复上述操作。
  • 当增量的大小减到1时,就相当于整个序列被分到一组,进行一次直接插入排序,排序完成。

为什么要让 gap 由大到小呢?

  • gap 越大,数据挪动得越快。
  • gap 越小,数据挪动得越慢。
  • 前期让 gap 较大,可以让大的数据可以更快到最后,小的数可以更快到前面,减少挪动次数。

一般情况下,取序列的一半作为增量,然后依次减半,直到增量为 1

给定一组无序数组如下:
在这里插入图片描述

第一轮,我们用序列长度的一半作为第一次排序时 gap 的值,此时相隔距离为 5 的元素被分为一组(共分了 5 组,每组有 2 个元素),然后分别对每一组进行直接插入排序。
C/C++排序算法(一) —— 插入排序和希尔排序_第15张图片

第二轮,gap 的值折半,此时相隔距离为 2 的元素被分为一组(共分了 2 组,每组有 5 个元素),然后再分别对每一组进行直接插入排序。
C/C++排序算法(一) —— 插入排序和希尔排序_第16张图片

第三轮,gap 的值再次减半,此时 gap 减为 1,即整个序列被分为一组,进行一次直接插入排序。
C/C++排序算法(一) —— 插入排序和希尔排序_第17张图片

上面过程中,前两趟就是希尔排序的预排序,最后一趟就是希尔排序的直接插入排序。

动图演示

我们来看一组动图演示

代码实现

代码示例

/*希尔排序
* 时间复杂度:O(N)
* 如果gap越小,越接近有序;
* gap越大,那么大的数据可以更快到最后,小的数可以更快到前面,但它不接近有序
*/
void ShellSort(int* a, int n) {
	//1. gap>1 预排序
	//2. gap == 1 直接插入排序
	int gap = n;
	while (gap > 1) {
		gap = gap / 3 + 1;
		//进行一趟排序
		for (int i = 0; i < n - gap; ++i) {
			int end = i;
			int tmp = a[end + gap];
			while (end >= 0)
			{
				if (tmp < a[end]) {
					a[end + gap] = a[end];
					end -= gap;
				}
				else {
					break;
				}
			}
			a[end + gap] = tmp;
		}
	}
}

复杂度分析

特性总结:

(1)希尔排序是对直接插入排序的优化。

(2)当 gap > 1 时都是预排序,目的是让数组更接近于有序。当 gap == 1 时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。

(3)希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好些树中给出的希尔排序的时间复杂度都不固定:

C/C++排序算法(一) —— 插入排序和希尔排序_第18张图片

C/C++排序算法(一) —— 插入排序和希尔排序_第19张图片

因为我们的 gap 是按照 Knuth 提出的方式取值的,而且 Knuth 进行了大量的试验统计,我们暂时就按照: O ( n 1.25 ) O(n^{1.25}) O(n1.25) O ( 1.6 ∗ n 1.25 ) O(1.6*n^{1.25}) O(1.6n1.25) 来计算。

时间复杂度: O ( N ∗ l o g N ) O(N*logN) O(NlogN),平均时间复杂度: O ( n 1.3 ) O(n^{1.3}) O(n1.3)

空间复杂度: O ( 1 ) O(1) O(1)

3. 总结

C/C++排序算法(一) —— 插入排序和希尔排序_第20张图片

你可能感兴趣的:(数据结构艺术,排序算法,算法,数据结构,c语言)