算法—希尔排序

概念

希尔排序(Shell’s Sort)是插入排序的一种又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因D.L.Shell于1959年提出而得名。
希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。 ——[摘自百度百科]

它出现的原因是,插入排序由于效率太低O(n^2),而它的效率是在当时第一批打破O(n^2)的算法O(n^1.25),因为插入排序擅长处理长度较短的, 部分有序(或有序)的数组,所以希尔排序在插入排序之前,先将数列排来差不多,相对整齐之后,在最后一次插入排序的时候效率就相对高一些,避免了无差别的插入排序无脑对比相邻俩元素的情况发生。

温馨提示:看这篇之前最好先搞懂插入排序:https://blog.csdn.net/user11223344abc/article/details/81567579

理解

看了这个图,我也只有个大概的感性认知,简单的说,它就是根据数组长度计算出的一个变量h为步长,分h趟进行的插入排序。它的复杂度是随着h来定的,那么这个h该如何取值呢?目前最优解是h = 3 * h + 1,h初始值为1,取最接近数组长度的h值作为初始步长,而后,再进行递减。
PS:由于h值与希尔排序的复杂度是息息相关的,所以它取值的最优解,这个问题是个数学问题,目前我是跟随业界标准的。

分析

比如这样一个数组:

int a[] = { 500,86,1,77,55,24,9 };

  • 1.它的长度是7
  • 2.带入h值目前最优解公式:h = 3 * h + 1,得出结果:h = 1,4,13 …
  • 3.由于4最接近长度7,所以取4为h初始值
  • 4.开始进行比较,那么我们画个图来揭示本例的比较流程

  • 5.那么我们推导一下这个h在排序过程中如何进行递减,因为 h 是 {1 , 4 , 13 , 40 ……},40/3 = 13(int自己舍位),13/3=4(int自己舍位),4/3=1(int自己舍位)。所以我们可以将来递减的公式推导为 h = h/3。

那么代码该如何来写呢?

1.h步长处理


    //希尔排序
    int * shellSort(int *a, int len) {

        int h = 1;

        //计算出步长的值
        while (h < len)
        {
            h = 3 * h + 1; 
        }
        h = h / 3;

        //进行希尔排序....
        while (h >= 1)
        {

            //TODO 排序逻辑...

            h = h / 3; //递减
        }

        return a;
    }

2.排序逻辑的编写

排序逻辑,总的来说,这个希尔排序相当于是当h值不为1时,一趟趟的只交换位置,当h为1时,进行普通的插入排序(插入排序我沿用了之前写的方法)。

另外,我这么写显得比较笨拙,但逻辑上相对简单,实际上还可以再进行优化,比如把h=1和h!=1的逻辑融合在一起,但实际上本质都一样,只不过多了常数级的代码,并没有什么大的影响,后续我会将优化版本更新上来,目前就先到这个程度。


    //进行希尔排序....
        while (h >= 1)
        {

            if (h == 1) {
                printArray(a, "最后一次插入排序之前:", len);
                insertionSort(a,len);
            }
            else
            {
                for (int i = 0; i + h < len ; i++)
                {
                    if (a[i] > a[i + h]) {
                        int temp = a[i];
                        a[i] = a[i + h];
                        a[i + h] = temp;
                    }
                }
            }

            h = h / 3; //递减
        }

Demo

运行结果:

希尔排序前
500,86,1,77,55,24,9,
最后一次插入排序之前
55,24,1,77,500,86,9,
希尔排序后
1,9,24,55,77,86,500,
请按任意键继续. . .

具体代码我不贴出来了,这里给出在线地址:
https://github.com/zj614android/algorithm/blob/master/Insertion_sort/Insertion_sort/ShellSort.cpp

Thanks

https://blog.csdn.net/ltyqljhwcm/article/details/53369718
https://www.imooc.com/article/21707
https://blog.csdn.net/jianfpeng241241/article/details/51707618
http://liuwangshu.cn/algorithm/2-insert-hill.html

猜你喜欢

转载自blog.csdn.net/user11223344abc/article/details/81698445