• 数据结构——排序算法——希尔排序


    希尔排序本质上是对插入排序的一种优化,它利用了插入排序的简单,又克服了插入排序每次只交换相邻两个元素的缺点。它的基本思想是:

    1.将待排序数组按照一定的间隔分为多个子数组,每组分别进行插入排序。这里按照间隔分组指的不是取连续的一段数组,而是每跳跃一定间隔取一个值组成一组
    2.逐渐缩小间隔进行下一轮排序
    3.最后一轮时,取间隔为 1,也就相当于直接使用插入排序。但这时经过前面的「宏观调控」,数组已经基本有序了,所以此时的插入排序只需进行少量交换便可完成。

    举个例子,对数组 [84,83,88,87,61,50,70,60,80,99]进行希尔排序的过程如下:

    请添加图片描述
    1.第一遍(5 间隔排序):按照间隔 5 分割子数组,共分成五组,分别是[84,50],[83,70],[88,60],[87,80],[61,99]。对它们进行插入排序,排序后它们分别变成: [50,84],[70,83],[60,88],[80,87],[61,99],此时整个数组变成 [50,70,60,80,61,84,83,88,87,99]

    2.第二遍(2 间隔排序):按照间隔2 分割子数组,共分成两组,分别是 [50,60,61,83,87],[70,80,84,88,99]。对他们进行插入排序,排序后它们分别变成:[50,60,61,83,87],[70,80,84,88,99],此时整个数组变成 [50,70,60,80,61,84,83,88,87,99]。这里有一个非常重要的性质:当我们完成 2 间隔排序后,这个数组仍然是保持 5 间隔有序的。也就是说,更小间隔的排序没有把上一步的结果变坏

    3.第三遍(1 间隔排序,等于直接插入排序):按照间隔 1 分割子数组,分成一组,也就是整个数组。对其进行插入排序,经过前两遍排序,数组已经基本有序了,所以这一步只需经过少量交换即可完成排序。排序后数组变成[50,60,61,70,80,83,84,87,88,99],整个排序完成。

     void shellSort(vector<int> arr) {
        // 间隔序列,在希尔排序中我们称之为增量序列
        for (int gap = arr.size() / 2; gap > 0; gap /= 2) {
            // 分组
            for (int groupStartIndex = 0; groupStartIndex < gap; groupStartIndex++) {
                // 插入排序
                for (int currentIndex = groupStartIndex + gap; currentIndex < arr.size(); currentIndex += gap) {
                    // currentNumber 站起来,开始找位置
                    int currentNumber = arr[currentIndex];
                    int preIndex = currentIndex - gap;
                    while (preIndex >= groupStartIndex && currentNumber < arr[preIndex]) {
                        // 向后挪位置
                        arr[preIndex + gap] = arr[preIndex];
                        preIndex -= gap;
                    }
                    // currentNumber 找到了自己的位置,坐下
                    arr[preIndex + gap] = currentNumber;
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    优化

     void shellSort(vector<int> arr) {
        // 间隔序列,在希尔排序中我们称之为增量序列
        for (int gap = arr.size() / 2; gap > 0; gap /= 2) {
            // 从 gap 开始,按照顺序将每个元素依次向前插入自己所在的组
            for (int i = gap; i < arr.size(); i++) {
                // currentNumber 站起来,开始找位置
                int currentNumber = arr[i];
                // 该组前一个数字的索引
                int preIndex = i - gap;
                while (preIndex >= 0 && currentNumber < arr[preIndex]) {
                    // 向后挪位置
                    arr[preIndex + gap] = arr[preIndex];
                    preIndex -= gap;
                }
                // currentNumber 找到了自己的位置,坐下
                arr[preIndex + gap] = currentNumber;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    使用 Knuth 序列进行希尔排序的代码

    void shellSortByKnuth(vector<int> arr) {
        // 找到当前数组需要用到的 Knuth 序列中的最大值
        int maxKnuthNumber = 1;
        while (maxKnuthNumber <= arr.size() / 3) 
        {
            maxKnuthNumber = maxKnuthNumber * 3 + 1;
        }
        // 增量按照 Knuth 序列规则依次递减
        for (int gap = maxKnuthNumber; gap > 0; gap = (gap - 1) / 3) {
            // 从 gap 开始,按照顺序将每个元素依次向前插入自己所在的组
            for (int i = gap; i < arr.size(); i++) {
                // currentNumber 站起来,开始找位置
                int currentNumber = arr[i];
                // 该组前一个数字的索引
                int preIndex = i - gap;
                while (preIndex >= 0 && currentNumber < arr[preIndex]) {
                    // 向后挪位置
                    arr[preIndex + gap] = arr[preIndex];
                    preIndex -= gap;
                }
                // currentNumber 找到了自己的位置,坐下
                arr[preIndex + gap] = currentNumber;
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
  • 相关阅读:
    Access2007中如何运行SQL执行SQl语句
    HDC2010+STM32读取数据发送到onenet平台
    【游戏测试工程师】初识游戏测试—你了解它吗?
    使用参数非参数和机器学习方法分析印度降雨变化,能给我国带来什么警示?
    Flink Watermark 机制
    matlab函数 状态空间系统ss、能控性矩阵ctrb、矩阵的秩rank、能控标准型canon、零极点配置place、系统极点pole等函数(线性定常系统)
    HTML5七夕情人节表白网页制作【爱心雨(满屏爱心飘落)】HTML+CSS+JavaScript
    java _JDBC 开发
    边缘检测生成(伪)手绘线稿风格的视频简易版教程
    java-php-net-python-金拱门自动订餐系统报告计算机毕业设计程序
  • 原文地址:https://blog.csdn.net/weixin_43945471/article/details/132836101