希尔排序

来源:互联网 发布:ios swift 项目源码 编辑:程序博客网 时间:2024/04/29 21:26

希尔排序

标签(空格分隔): 排序算法


希尔排序(Shell Sort),又称为“缩小增量排序”,是插入排序的一种更高效的改进的版本,因此希尔排序本质来说还是属于插入排序。需要注意,希尔排序是一种非稳定的排序算法。

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

  • 插入排序在对几乎已经排好序的数据操作时, 效率高,即可以达到线性排序的效率
  • 但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位

希尔排序基本思想

基本思想:

先将整个待排元素序列分割成若干个子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序。因为直接插入排序在元素基本有序的情况下(接近最好情况),效率是很高的,因此希尔排序在时间效率上比前两种方法有较大提高。

具体做法:

首先确定一组增量d0,d1,d2,d3,…,dt-1()其中n>d0>d1>…>dt-1=1),对于i=0,1,2,…,t-1,依次进行下面的各趟处理:根据当前增量di将n个元素分成di个组,每组中元素的下标相隔为di;再对各组中元素进行直接插入排序

实例演示

假设待排序文件有10个记录,其关键字分别是:
49,38,65,97,76,13,27,49,55,04。
增量序列的取值依次为:
5,3,1

排序过程:模拟动画演示

算法实现

 public class Shell{     public static void sort(Comparable[] a){        int N = a.length;        int h =1;        while(h<n/3) h=3*h+1;        while(h>=1){            for(int i=h;i<N;i++){                for(int j=i;j>=h&&less(a[j],a[j-h]);j-=h){                    exch(a,j,j-h);                }            }            h = h/3;        }    }    ......}

完整代码下载链接

算法分析

增量序列的选择

希尔排序的执行时间依赖于增量序列的选择,算法的性能不只是取决于h,还取决于h之间的数学性质,比如他们的公因子等,目前还没有办法证明某个序列是最好的,但是好的增量序列有一些共同的特征。

好的增量序列的特征

  • 最后一个增量必须为1;
  • 应该尽量避免序列中的值(尤其是相邻的值)互为倍数的情况。有人通过大量的实验,给出了目前较好的结果:当n较大时,比较和移动的次数约在nl.25到1.6n1.25之间。

本文算法实现中的增量序列计算跟使用都很简单,跟复杂的增量序列的性能相近,但可以证明的是复杂的增量序列,在最坏情况下的性能,优于我们所使用的递增序列。

稳定性

希尔排序是不稳定的

性能优于直接插入排序

希尔排序的时间性能优于直接插入排序的原因:

  • 当文件初态基本有序时直接插入排序所需的比较和移动次数均较少。
  • 当n值较小时,n和n2的差别也较小,即直接插入排序的最好时间复杂度O(n)和最坏时间复杂度0(n2)差别不大。
  • 在希尔排序开始时增量较大,分组较多,每组的记录数目少,故各组内直接插入较快,后来增量di逐渐缩小,分组数逐渐减少,而各组的记录数目逐渐增多,但由于已经按di-1作为距离排过序,使文件较接近于有序状态,所以新的一趟排序过程也较快。

有经验的程序员有时候会选择希尔排序,因为对于中规模的数组,希尔排序的性能还是可以接受的,而且希尔排序的代码比较简单,不需要额外的内存空间,存在其他更高效的算法,但是除了对于较大的N之外,他们可能只会比希尔排序快两倍,甚至不到,而且算法更复杂。如果你需要解决一个排序问题,但是有没有排序函数可以调用,那么你可以先选择希尔排序,然后再考虑是否值得将他替换为更复杂的算法。

参考资料

图书:《算法 第四版》
博客:
8大排序算法图文讲解
希尔排序基本思想

0 0
原创粉丝点击