11 插入排序和希尔排序

1. 插入排序

基本思想

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

把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列

在玩扑克牌时,就用了插入排序的思想

过程

类似扑克牌,手里有3,7,8,9,,四张牌,这时,如果摸到4,怎么排它的位置。先和9比,小于9,再和前面的8比,再和7比,再和3比,这时大于3,就找到了它该插入的位置,将4放在3的后面,其他的往后挪一个

从7开始排,第一个数肯定是有序的。

然后是4,4小于7,所以7应该在4的前面,将7往后挪一位,4放在7的位置

5比7小,7往后挪,5比4大,所以5的位置就是4的后面

...

这样不断比较,直到最后一个数排完

c 复制代码
void Sort(int ary[], int len)
{
	//第一个是有序的,从第二个开始
	for (int i = 1; i < len; i++)
	{
		//[0,end]的区间是有序的,end是待排序数的前一个下标
		int end = i - 1;
		int temp = ary[i];
		while (end >= 0)
		{
			if (ary[end] > temp)
			{
				ary[end + 1] = ary[end];
				end--;
			}
			else
			{
				break;
			}
		}
		//此时,end是前一个位置
		ary[end + 1] = temp;
	}
	
}

特性总结:

1.元素集合越接近有序,直接插入排序算法的时间效率越高

2.时间复杂度:最坏O(N^2^),逆序的时候最坏,最好是O(N),正序的时候

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

4.稳定性:稳定

插入和冒泡

两者在最好和最坏的情况下时间复杂度相同,但对于大部分有序,局部无序的情况下,插入的适应性更强

上面的数据只有最后面的9和8无序,对于冒泡排序来说,需要第一轮比过去,交换8和9,第二轮到9的位置后发现有序,排序完毕。插入排序来说,检查到8的时候,一交换,排序完毕。这时,插入排序的效率高。冒泡排序更容易理解,比较容易上手

2. 希尔排序

基本思想

希尔排序又称缩小增量法,基本思想是:先选定一个整数,把待排序文件中所有记录分成几个组,对每一组内的记录进行排序。然后缩小分组间隔。重复上述过程,当增量缩小到1时,所有记录排为一组,排好序

插入排序对大部分有序的数据效率很高,但对于倒序的效率很低。可以根据这个特性优化。先对数据用一种效率高的方法进行预排序,使数据接近有序。最后再进行一次插入排序,就可以排序成功

过程

首先将整个数组按间隔gap分组,分为gap组,这里以间隔3分组

上面的从9开始每间隔3分为归为一组,9,6,3,1分到了一组

然后从8开始分组,直到将所有数据分组完

以颜色区分分为了3组,首先写一个类似的插入排序对红色组排序

c 复制代码
void ShellSort(int ary[], int len)
{
//间隔3
	int gap = 3;
//0,从end+gap开始往前对比,到组内最后一个数排完,每次加间距
		for (int i = 0; i < len - gap; i += gap)
		{
			int end = i;
			int temp = ary[gap + end];
//将gap+end下标,也就是组内下一个数和前面的所有对比交换
			while (end >= 0)
			{
				if (ary[end] > temp)
				{
					ary[gap + end] = ary[end];
					end = end - gap;
				}
				else
				{
					break;
				}
			}

			ary[end + gap] = temp;
		}	
}

将红色组排好了序,接着将其他两组也排好序,意味着需要循环组数的次数,控制变量i

c 复制代码
	int gap = 3;
	//循环gap组
	for (int j = 0; j < gap; j++)
	{
		//从end+gap开始往前对比,找组内下一个数,每次加间距
		for (int i = j; i < len - gap; i += gap)
		{
			int end = i;
			int temp = ary[gap + end];
			//将gap+end下标,也就是组内下一个数和前面的所有对比交换
			while (end >= 0)
			{
				if (ary[end] > temp)
				{
					ary[gap + end] = ary[end];
					end = end - gap;
				}
				else
				{
					break;
				}
			}

			ary[end + gap] = temp;
		}
	}
	
}

上面的数字仍是无序的,但经过预排序已经大部分有序

上面的三层循环可以减到2层循环,最外层的循环可以去掉。实际上外面两层循环本质上是把所有数据遍历了一遍,所以可以只用一层,多组并排,i每次递增1

c 复制代码
void ShellSort(int ary[], int len)
{
	int gap = 3;

	//从end+gap开始往前对比,找组内下一个数,每次加间距
	for (int i = 0; i < len - gap; i++)
	{
		int end = i;
		int temp = ary[gap + end];
		//将gap+end下标,也就是组内下一个数和前面的所有对比交换
		while (end >= 0)
		{
			if (ary[end] > temp)
			{
				ary[gap + end] = ary[end];
				end = end - gap;
			}
			else
			{
				break;
			}
		}

		ary[end + gap] = temp;
	}
}

怎么让大部分有序的数据变为有序,只需要控制gap的变化,当gap=1时,就是插入排序。gap应该根据数据量变化,数据量大的时候,gap的跳跃变大,gap越大,越接近无序,但可以更快的将更大更小的数放在两端的位置

为了使排的数据最终变有序,gap的值最后必须可以变为1,官方给出的是gap每次/3+ 1,当gap为2时,就变为0,所以/3后再加1,这样无论多少数据,最终都会分为一组

c 复制代码
void ShellSort(int ary[], int len)
{
	int gap = len;
	//gap>1预排序
	//gap=1插入排序
	while (gap > 1)
	{
		//+1保证最后一次是1
		//gap = gap/2
		gap = gap / 3 + 1;
		//从end+gap开始往前对比,找组内下一个数,每次加间距
		for (int i = 0; i < len - gap; i++)
		{
			int end = i;
			int temp = ary[gap + end];
			//将gap+end下标,也就是组内下一个数和前面的所有对比交换
			while (end >= 0)
			{
				if (ary[end] > temp)
				{
					ary[gap + end] = ary[end];
					end = end - gap;
				}
				else
				{
					break;
				}
			}

			ary[end + gap] = temp;
		}
	}
	
}

时间复杂度

gap/3这一层时间复杂度log~3~N,因为每次都除以3。而对于里面的两个循环,时间复杂度计算比较麻烦,大致可认为是O(N)。因为gap每次都是变化的,且前面的排序对后面排序的增益效果无法估算

上面只能以最坏的情况计算,但gap越小,情况应该是越好的

特性

1.希尔排序是对直接插入排序的优化

2.当gap>1时是预排序,目的是让数组更接近有序。gap==1时,数组已经接近有序了,就会很快

3.时间复杂度: O(N^1.3^)

3.稳定性: 不稳定

3. 插入排序和希尔排序比较

随机生成10万个数字,计算两个排序所消耗的时间,大致差了100倍

相关推荐
代码雕刻家23 分钟前
数据结构-3.9.栈在递归中的应用
c语言·数据结构·算法
雨中rain23 分钟前
算法 | 位运算(哈希思想)
算法
Kalika0-02 小时前
猴子吃桃-C语言
c语言·开发语言·数据结构·算法
代码雕刻家2 小时前
课设实验-数据结构-单链表-文教文化用品品牌
c语言·开发语言·数据结构
龙图:会赢的2 小时前
[C语言]--编译和链接
c语言·开发语言
sp_fyf_20242 小时前
计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-02
人工智能·神经网络·算法·计算机视觉·语言模型·自然语言处理·数据挖掘
小字节,大梦想3 小时前
【C++】二叉搜索树
数据结构·c++
Cons.W4 小时前
Codeforces Round 975 (Div. 1) C. Tree Pruning
c语言·开发语言·剪枝
我是哈哈hh4 小时前
专题十_穷举vs暴搜vs深搜vs回溯vs剪枝_二叉树的深度优先搜索_算法专题详细总结
服务器·数据结构·c++·算法·机器学习·深度优先·剪枝
Tisfy4 小时前
LeetCode 2187.完成旅途的最少时间:二分查找
算法·leetcode·二分查找·题解·二分