【经典排序算法】4. 希尔排序

希尔排序是插入排序的一种改进版本。

可以看做是一种分组的插入排序,分组的变化受小组尾索引 i i i 和分组步长 s t e p step step控制,分好组后,对每个小组的元素执行插入排序即可。

时间复杂度分析:(希尔排序的时间性能优于插入排序的原因:)

  1. 当数组初态基本有序时直接插入排序所需的比较和移动次数都较少。

  2. 当n值较小时,n和n2的差别也较小,即直接插入排序的最好时间复杂度O(N)和最坏时间复杂度O(N2)差别不大。

  3. 在希尔排序刚开始元素很无序的时候,步长step较大,分组较多,每组的元素数目少,所以组内插入排序速度很快。后面当元素基本有序了,步长step较小,虽然分组数减少,每组的元素数量多,但由于数组较接近于有序状态,所以新的一趟插入排序效率较高。

稳定性:
一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同组的插入排序中,相同元素可能在各自的插入排序中移动,最后稳定性就会被打乱,所以希尔排序是不稳定的。

应用:
希尔算法在最坏的情况下和平均情况下执行效率相差不是很多,与此同时快速排序在最坏的情况下执行的效率会非常差。因此可以在几乎任何排序工作刚开始时,先使用希尔排序,若在实际使用中证明它不够快,再改成快速排序这样更高级的排序算法。

代码如下:

public class Main {

    public static void main(String[] args) {
        int[] arr = {3, 3, 5, 6, 2, 1};
        System.out.print("排序前:");
        arrPrint(arr);
        ShellSort(arr);
        System.out.print("排序后:");
        arrPrint(arr);
    }

    // 希尔排序
    // 希尔排序是插入排序的改进版,意为将数组arr按照递减的步长step分组,
    // 同组的元素距离相邻一个step的长度,小组的尾索引记为i,每组分别使用插入排序,
    // 每次排序完步长step将按照减半来递减,构成新分组,再每组分别进行插入排序,
    // 以此循环下去,直至step=1,分组到最后只剩1组,小组尾索引i不断右移,即遍历完数组arr
    //
    // 实例中step将分别等于3,1。(想看过程可以利用arrPrint函数将中间过程打印)
    // 第一层for循环将step初始化为数组长度的一半,后循环减半,
    // 第二层for循环,锚定分组后每组的尾索引为i=j+step。由于同组元素距离为一个step,
    // 所以同组的元素可以通过j循环减step获得。
    public static void ShellSort(int[] arr) {
        int len = arr.length;
        for (int step = len / 2; step >= 1; step /= 2) {
            GroupInsertSort(arr, len, step);
        }
    }

    // (分组)插入排序
    // 还记得原来插入排序中i是从1开始的,就相当于分组后的i是从step开始的。
    // 遍历数为arr[i]=temp,这也就是待排序元素。
    // j = i - step为同组的i前一个元素索引,根据插入排序算法,
    // 如果j = i - step索引的元素arr[i]大于i索引的元素temp,
    // 则在同组内用while循环将大于temp的左边元素全部右移。
    // 注意指针要移动一个step的距离。
    private static void GroupInsertSort(int[] arr, int len, int step) {
        for (int i = step; i < len; i++) {
            int j = i - step;
            int temp = arr[j + step];
            while (j >= 0 && arr[j] > temp) {
                arr[j + step] = arr[j];
                j -= step;
            }
            if (j != i - step)
                arr[j + step] = temp;
        }
    }

    // 辅助函数:将int[] 打印出来
    private static void arrPrint(int[] arr) {
        StringBuilder str = new StringBuilder();
        str.append("[");
        for (int v : arr) {
            str.append(v + ", ");
        }
        str.delete(str.length() - 2, str.length());
        str.append("]");
        System.out.println(str.toString());
    }
}


实例的动画演示如下:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

锥栗

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

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

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

打赏作者

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

抵扣说明:

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

余额充值