Loading

希尔排序

希尔排序

希尔排序步骤

对于一个序列,首先将它按一个数字做间隔分组,比如我们提取第一个数字81,然后间隔5个位置,取到35,再间隔5个位置取到41,再间隔5个位置超过了数组的边界。81,35,41(上图第一行中蓝色部分),对这三个数字构成的序列做一个插入排序=》35,41,81。

然后在上面排序后的数组的基础上,缩小间隔,这次每隔3个间隔取一个数,构成序列:35,28,75,58,95(上图第二行的蓝色部分),再做插入排序。

最后1间隔,对所有的数做插入排序。

会发现在最后做1间隔排序的时候,整个序列已经基本有序。这对插入排序非常友好,因为如果序列基本有序,那就意味着逆序对较少,插入排序的效率接近O(n)。

这就是希尔排序,我们设置一个间隔序列5-3-1,逐步使序列变得有序。并且发现做完5间隔的排序后,再做3间隔排序,并不会对5间隔的排序产生影响,也就是:

K-间隔有序的序列,在执行K-1间隔排序后,仍然是K间隔有序的。

有一个最坏的情况:

做8间隔排序,发现序列已经有序,无序改变;

做4间隔排序,发现序列已经有序,无序改变;

做2间隔排序,发现序列已经有序,无序改变;

最后不得不做1间隔排序,即所有的元素做插入排序。

也就是说前面的N间隔排序没有起到效果。说明:

增量元素不互质,则小增量可能根本起不到作用。

因此,有人研究出一些经典的增量序列:

发现时间复杂度的证明很困难。

Java代码实现

public void sort(int[] arr) {
    // 定义一个增量序列
    int[] seqs = {5, 3, 1};

    for (int seq : seqs) {
        // 下面就是插入排序
        // 默认第一个数是有序的
        for (int i = 1; i < arr.length; i++) {
            int temp = i;
           	// 每次新进来一个数,把这个数和左边已经排好序的部分比较
            for (int j = i; j > 0; j -= seq) {// 注意,这里不是j--,而是减去一个序列数
                // 如果新进来的数比相邻左边的数小,那就把左边的数往右挪一位
                if (arr[j] < arr[j - 1]) {
                    arr[j] = arr[j - 1];
                    temp = j - 1;
                }
            }
            // 找到空位,插入
            if (temp != i) {
                arr[temp] = i;
            }
        }
    }
}

优点:

数据量大的时候效果好,使用特定增量序列的最坏时间复杂度比简单排序O(n^2)好。

缺点:

不是稳定排序。

posted @ 2021-11-06 20:35  非凡岁月  阅读(228)  评论(0编辑  收藏  举报