插入排序算法:直接插入排序和希尔排序

前言

  • 本篇博客主要介绍插入排序算法中的直接插入排序(InsertSort)和希尔排序(ShellSort)
  • 实现代码:C语言

 常见排序算法


常见的排序算法主要有这几类,下面开始介绍插入排序,而其他三种排序会整理在后面的博客中


直接插入排序

✨基本思想:

        直接插入排序(InsertSort)是一种简单的插入排序法,其基本思想是:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列 。

🔶简单来说就是原来的序列有序,每次插入一个,继续保持有序,直到插入结束。

💭ps:这里可以类比我们平时打斗地主时,摸排并插入已经原来已经排好的牌中,这个操作过程就运用了直接插入排序的思想。


🎆🎆实现步骤:

前提:给定一组有N个元素的待排序列,要求排升序

  1. 从第一个元素开始,认为只有一个元素的序列有序

  2. 取下一个元素存放到临时变量tmp中

  3. 对前面已经排好序的序列从后面开始往前遍历,对于排升序,把遍历过程中所有大于tmp的数据以此往后挪,直到找到第一个比tmp小的数据(若是要求排降序,反过来操作就行)

  4. 将tmp插入到当前元素的后面,完成一次排序操作

  5. 接下来继续选择下一个数据,重复2~4步骤,直到操作完待排序列的最后一个数

下面引用网上的一张动gif来演示操作


 🎉🎉🎉代码如下:

void InsertSort(int* a, int n)
{
	assert(n);

	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;
			}
		}
		a[end + 1] = tmp;
	}
}

🔔🔔🔔🔔特性总结:

  1. 待排序列越接近有序,直接插入排序算法的时间效率越高(ps:后面要介绍的希尔排序就是基于这一点对直接插入排序的优化)
  2. 时间复杂度:O(N^2),最坏情况,待排序列为逆序,或者接近逆序,为O(N^2);最好情况,待排序列为升序,或者接近升序,为O(N)
  3. 空间复杂度:O(1),它是一种稳定的排序算法
  4. 稳定性:稳定

 希尔排序(缩小增量排序)

✨基本思想:

        希尔排序法又称缩小增量法。希尔排序法的基本思想是:先选定一个整数gap,把待排序文件中所有距离为gap的记录分在同一组内,并对每一组内的记录进行排序。然后,取新的gap,重复上述分组和排序的工作。当到达gap=1时,所有记录在同一组内排好序。

🔶分为两个步骤:当gap > 1 时,进行的是预排序操作;当gap == 1 时,进行直接插入排序。简单来说就是在进行直接插入排序之前,先对待排序列进行预排序操作,使它更接近于有序序列。


🎆🎆实现步骤:

前提:给定一组有N个元素的待排序列,要求排升序

  1. 先选定一个小于N的整数gap作为作为第一个增量(ps:gap是和N相关的数,初始化为gap == N,然后再选取更新方式)
  2. 然后将所有距离为gap是元素分在同一组(或者说只访问距离为gap的元素),对每一组的元素进行直接插入排序。
  3. 然后再取一个新的比第一增量小的整数作为第二增量,更新gap,重复2步骤。
  4. 当增量的大小减小到1时(即gap == 1 时),就相当于整个序列被分到同一组,进行一次直接插入排序,排序结束。
  • 这里要注意的是gap的选取和更新,一般有以下两种选择
  1. gap初始化为N,gap = gap / 3 + 1;gap每次都变成前面的三分之一,最后加1是为了控制最终结束时gap == 1(推荐使用
  2. gap初始化为N,gap = gap / 2;gap每次减少为二分之一,最后也必将达到gap == 1

下面引用网上的一张动gif来演示操作

动图可能不是很方便看和理解。这里给大家弄了一张流程图,看着这张图跟着上面的思路走一遍,应该能更好的理解算法思路


 🎉🎉🎉代码如下:

void ShellSort(int* a, int n)
{
    assert(n);

	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;//由于每次gap/3,加1能保证最后一次gap必定是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的取值方法很多,导致很难去计算,因此在好些书中给出的希尔排序的时间复杂度都不固定。最接近的时间复杂度为:O(N^1.3)
  4. 稳定性:不稳定

至于插入排序的时间测试,我留在了排序算法的最后一篇博客中 :💫💫传送门


学习记录:

  • 本篇博客整理于2022.7.8
  •  请多多指教🌹🌹
  • 如果觉得写的不错,看完了别忘了点赞啊,感谢支持😏😏
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

如何写出最优雅的代码

感谢支持,我将继续努力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值