直接插入排序到希尔排序做的那些改进

Posted Python与算法社区

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了直接插入排序到希尔排序做的那些改进相关的知识,希望对你有一定的参考价值。

主要推送关于对算法的思考以及应用的消息。坚信学会如何思考一个算法比单纯地掌握100个知识点重要100倍。本着严谨和准确的态度,目标是撰写实用和启发性的文章,欢迎您的关注,让我们一起进步吧。



01

你会学到什么?


彻底弄明白常用的排序算法的基本思想,算法的时间和空间复杂度,以及如何选择这些排序算法,确定要解决的问题的最佳排序算法,已经总结了,,下面总结直接插入排序到希尔排序做的改进,后面再继续总结归并排序和基数排序。



02

讨论的问题是什么?

各种排序算法的基本思想;讨论各种排序算法的时间、空间复杂度;以及算法的稳定性;算法是如何改进的,比如冒泡排序如何改进成了目前最常用的快速排序的,直接选择排序到堆排序的改进,接下来要讨论直接插入排序到希尔排序做的优化。


03

相关的概念和理论

内部排序

若整个排序过程不需要访问外存便能完成,则称此类排序问题为内部排序。


外部排序

若参加排序的记录数量很大,整个序列的排序过程不可能在内存中完成,则称此类排序问题为外部排序。


就地排序

若排序算法所需的辅助空间并不依赖于问题的规模n,即辅助空间为O(1),称为就地排序。


稳定排序

假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序后,这些记录的相对次序保持不变,即在原序列中 ri=rj, ri 在 rj 之前,而在排序后的序列中,ri 仍在 rj 之前,则称这种排序算法是稳定的;否则称为不稳定的。


排序序列分布

排序需要考虑待排序关键字的分布情况,这会影响对排序算法的选择,通常我们在分析下列算法时都考虑关键字分布是随机分布的,不是按照某种规律分布的,比如正态分布等。


待排序序列

排序序列中,剩余即将要排序的序列部分。


已排序序列

排序序列中,已经排序好的序列部分。


哨兵

一切为简化边界条件而引入的附加结点(元素)均可称为哨兵


04

直接插入排序

直接插入排序,英文名称 straight insertion sort,它是一种依次将无序区的元素在有序区内找到合适位置依次插入的算法。


基本思想

每次从无序表中取出第一个元素,把它插入到有序表的合适位置,使有序表仍然有序,直到无序表内所有元素插入为止。

首先在当前有序区R[0..i-1]中查找R[i]的正确插入位置 k(0≤k≤i-1);

然后将R[k..i-1]中的记录均后移一个位置,腾出 k 位置上的空间插入R[i]。


直接插入排序举例

我们仍然用待排序列   

 3    2    5    9    2

演示直接插入排序的过程,

直接插入排序到希尔排序做的那些改进

至此结束插入排序的过程,可以看到直接插入排序共经过4轮的操作,有些轮需要经过找到元素的合适位置,同时移动插入点后元素到有序区元素依次向后移动一个位置,有些轮不需要移动数据元素,直接将待插入元素插入到有序区的最后一个位置。


05

算法评价

如果目标是把n个元素的序列升序排列,那么采用插入排序存在最好情况和最坏情况。


最好情况就是,序列已经是升序排列了,在这种情况下,需要进行的比较操作需(n-1)次即可。


最坏情况就是,序列是降序排列,那么此时需要进行的比较共有n(n-1)/2次。


插入排序从上个演示中可以看到直接插入排序是稳定的排序算法,每次找到的插入点位置定下一个规则,要么统一放在相等关键码的前面或后面。


插入排序算法平均来说时间复杂度为O(n^2)比较次数越多,插入点后的数据移动越多(看下演示中的步骤4),特别是当数据总量庞大的时候,但是可以用链表解决数据移动的问题


因而,插入排序不适合对于数据量比较大的排序应用。直接插入排序在n不大时,插入排序的效果会很好,但是,如果需要排序的数据量很大直接插入排序的性能大幅下降,那么有没有优化的方法呢?由此诞生了插入思想下的希尔排序。


06

分组插入的希尔排序

由希尔在1959年提出,称为希尔排序(shell sort),也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。


希尔排序是基于直接插入排序的以下两点性质而提出改进方法的:

  1. 直接插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率。就是上节提到的,最好情况就是,序列已经是升序排列了,在这种情况下,需要进行的比较操作需(n-1)次即可

  2. 直接插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位,如上节举的直接插入排序的例子,一轮只能操作一个数据。


希尔排序的关键概念---增量序列

是指在待排序序列中提取关键码所用的序号间隔,比如初始序列包含5个元素 

[3 2 5 9  2], 如果增量序列为2,那么在一轮排序中,分为两组: [3 5  2] ,[2 9] ,在这两组中分别做直接插入排序。


算法思想

  1. 先取一个正整数 d1<n,把所有序号相隔 d1 的数组元素放一组,组内进行直接插入排序,

  2. 然后取 d2< d1,重复上述分组和直接插入排序操作;

  3. 直至di = 1,即所有记录放进一个组中排序为止。


非降序排序举例

我们仍然用待排序列   

 3    2    5    9    2

进行非降序排序操作,一般的初次取序列的一半为增量,以后每次减半,直到增量为1,

直接插入排序到希尔排序做的那些改进

在第一轮中,选取增量为2,即分为两组,第一组为 [3  5  2] ,另一组为 [2  9 ] ,分别对这两组做直接插入排序,第一组插入排序的结果为[2  3  5],第二组不动,这样导致的直接结果是原来位于最后的2经过第一轮插入排序后,跑到最头里了,这样两个2的相对位置发生改变了,所以希尔排序不是稳定的排序算法


再经过第二轮排序,此时的增量为1,所以一共只有一组了,相当于直接插入排序,9后移1步,5插入到原9的位置。


这样整个的希尔排序结束,得到如上图所示的非降序序列。


算法评价

希尔排序的时间复杂度为 O(logn * logn * n), 没有快速排序算法O(n*logn)快 ,因此中等大小规模表现良好,对规模非常大的数据排序不是最优选择。


但是比直接插入排序 O(n^2)复杂度算法快得多,并且希尔排序非常容易实现,算法代码短而简单。 


此外,希尔算法在最坏的情况下和平均情况下执行效率相差不是很多,与此同时快速排序在最坏 的情况下执行的效率会非常差。


专家门提倡,几乎任何排序工作在开始时都可以用希尔排序,若在实际使用中证明它不够快, 再改成快速排序这样更高级的排序算法。



07

总结

希尔排序算法利用分组插入排序技术,仅仅这一分组改进,减少了其复制的次数,速度要比直接插入排序快很多。当n值很大时数据项每一趟排序需要移动的个数很少,但数据项的距离很长。当n值减小时每一趟需要移动的数据增多,此时已经接近于它们排序后的最终位置。 正是这两种情况的结合才使希尔排序效率比插入排序高很多。


Shell算法的性能与所选取的分组长度序列有很大关系。只对特定的待排序记录序列,可以准确地估算关键码的比较次数和移动次数。


想要弄清关键码比较次数和移动次数与增量选择之间的关系,并给出完整的数学分析,至今仍然是数学难题。





以上是关于直接插入排序到希尔排序做的那些改进的主要内容,如果未能解决你的问题,请参考以下文章

面试官:手写一个希尔排序,并对其改进

排序 | 希尔排序

4希尔排序

希尔排序

希尔排序

算法:希尔排序