排序算法之希尔排序

希尔排序

1959年由唐纳德·希尔(Donald Shell)提出希尔排序。

希尔排序的思想:把数组中的元素看作是一个矩阵,分成m列,逐列进行排序(一般采用插入排序),m从某个整数逐渐减为1,当m为1时,整个序列将完全有序。因此,希尔排序也被称为递减增量排序(Diminishing Increment Sort)。

矩阵的列数取决于步长序列(step sequence),比如,如果步长序列为{1,5,19,41,109,…},就代表依次分成109列、41列、19列、5列、1列进行排序。不同的步长序列,执行效率也不同。

实例

希尔本人给出的步长序列是n/2^k(其中n为数组长度),例如要对下面的数组进行排序,n为16,步长序列是{1, 2, 4, 8}:

在这里插入图片描述

首先分成8列进行排序:

在这里插入图片描述

然后再分成4列进行排序:

在这里插入图片描述

再分成2列进行排序:

在这里插入图片描述

最后分成一列进行排序:

在这里插入图片描述
最后数组中的元素就变成有序的了。

疑惑:直接分成一列进行排序不就行了,为什么要分成这么多列?不难看出来,从8列变为1列的过程中,逆序对的数量在逐渐减少,而希尔排序底层会使用插入排序,插入排序的时间复杂度与逆序对的数量成正比,这样就可以提高插入排序的效率。

因此希尔排序底层一般使用插入排序对每一列进行排序,也很多资料认为希尔排序是插入排序的改进版。

动图演示

在这里插入图片描述

代码实现

    protected void sort() {

        List<Integer> stepSequence = shellStepSequence();
        
        for (Integer step : stepSequence) {
            sort(step);
        }

    }

    private void sort(Integer step) {

        // 对每一列进行插入排序
        for (int col = 0; col < step; col++) {

            // 插入排序
            for (int begin = col + step; begin < array.length; begin += step) {
                int cur = begin;
                E v = array[cur];
                while (cur > col && cmp(v, array[cur - step]) < 0) {
                    array[cur] = array[cur - step];
                    cur -= step;
                }
                array[cur] = v;
            }
        }
    }

    private List<Integer> shellStepSequence() {

        // n/2^k
        int n = array.length;
        List<Integer> list = new ArrayList<>();

        while ((n >>= 1) > 0) {
            list.add(n);
        }

        return list;
    }

复杂度与稳定性

最好情况是步长序列只有1,且序列几乎有序,时间复杂度为O(n)。

希尔本人给出的步长序列,最坏情况时间复杂度是 O(n^2)。

希尔排序的时间复杂度与步长序列有关。

更多精彩内容关注本人公众号:架构师升级之路
在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

morris131

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值