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

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

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,整个排序完成。

cpp 复制代码
 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;
            }
        }
    }
}

优化

cpp 复制代码
 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;
        }
    }
}

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

cpp 复制代码
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;
        }
    }
}
相关推荐
喜欢吃燃面33 分钟前
深入 C++ STL:从 unordered_set与unordered_map到底层哈希表的原理与实现
数据结构·c++·散列表
en.en..1 小时前
Linux fork() 工作原理
数据结构·算法
地平线开发者2 小时前
bevformer算法模型详细解读
算法
liliangcsdn2 小时前
ICIR权重矩阵如何加权标准化为综合因子
算法
SDWAN_Cheap2 小时前
SD-WAN智能选路技术的算法与实现机制
算法·php·sdwan
不会就选b3 小时前
数据结构之树&&二叉树(二)
数据结构·算法
渡之3 小时前
ArduPilot (APM)滤波器之SlewLimiter 深度解析
算法
zander2584 小时前
LeetCode 1143. 最长公共子序列
开发语言·python·算法
Sumerking4 小时前
llc_control.c 专项评审(v3 更新版)
c语言·开发语言·算法·obc
软行4 小时前
LeetCode 每日一题 3876. 构造奇偶一致的数组 II
c++·算法·leetcode