[排序算法]希尔排序

前言

希尔排序是对直接插入排序的优化,其思路是先创造"基本有序"的条件,再进行精细的插入排序

如果你还没有学会直接插入排序算法,那请先看博主前一篇文章:[排序算法]直接插入排序

学完直接插入排序,我们不难得出其优缺点:

  1. 优点 :当待排序序列基本有序 时,直接插入排序的效率非常高,近似 O(n)

  2. 缺点 :当待排序序列完全逆序 时,效率极低,为 O(n²)。因为每次插入一个新元素,都需要与前面所有已排序的元素进行比较和移动。

我们希尔排序就是为了降低缺点的影响,通过预处理,使待排序序列"基本有序"。

基本思想

希尔排序是对直接插入排序的优化,其核心思想是通过一个递减的步长序列(gap sequence)将数组分成若干子序列,对每个子序列进行插入排序,使数组逐渐"基本有序",最后当步长为1时进行直接插入排序。这种预处理能有效减少直接插入排序中的比较和移动次数,从而提升整体效率。
有点懵?先不要慌!下面咱们逐步分析。

具体步骤:

  1. 选择一个初始步长gap(通常为n/2或n/3)。

  2. 将数组中间隔为gap的元素分在同一组,形成多个子序列。

  3. 对每个子序列进行插入排序。

  4. 缩小gap(例如,使用gap = gap / 3 + 1),重复步骤2-3,直到gap=1。

  5. 当gap=1时,执行一次直接插入排序,此时数组已基本有序,排序效率很高。

打个比方:

详解代码

cpp 复制代码
//希尔排序
void ShellSort(int* a, int n)
{
    //gap是步长,gap的值就是每组数据间隔
    //比如第一组第一个数据为arr[i]那下一个即为arr[i+gap]
    int gap = n;
    while (gap > 1)
    {
        //推荐写法:除3
        gap = gap / 3 + 1;
        //i如果大于等于n-gap,会导致a[end+gap]越界访问
        for (int i = 0; i < n - gap; i++)
        {
            //从第一个元素开始
            //end含义为已排序部分的最后一个元素的下标
            int end = i;
            //tmp为该组下一个元素的值
            int tmp = a[end + gap];
            //end如果小于0,则证明a[end+gap]已经和该组第一个元素互换了值
            while (end >= 0)
            {
                //同一组中,若前面的数大于后面的
                if (a[end] > tmp)
                {
                    //后面的值等于前面的
                    a[end + gap] = a[end];
                    //end自身减gap
                    end -= gap;
                }
                //否则证明已经有序
                else
                {
                    break;
                }
            }
            //两种情况,if满足一种,不满足一种
            a[end + gap] = tmp;
        }
    }
}

希尔排序的时间复杂度范围大致在 O(n log²n) 到 O(n²) 之间。通过选择优秀的增量序列,其平均时间复杂度可以优化到大约 O(n^1.3) 到 O(n^1.5),这明显优于 O(n²) 的直接插入排序。
希尔排序时间复杂度不好计算,因为 gap 的取值很多,导致很难去计算,因此很多书中给出的希尔排序的时间复杂度都不固定。《数据结构(C语言版)》--- 严蔚敏书中给出的时间复杂度为:


-------有问题欢迎私信和评论------

相关推荐
草履虫建模13 小时前
力扣算法 1768. 交替合并字符串
java·开发语言·算法·leetcode·职场和发展·idea·基础
naruto_lnq15 小时前
分布式系统安全通信
开发语言·c++·算法
Jasmine_llq15 小时前
《P3157 [CQOI2011] 动态逆序对》
算法·cdq 分治·动态问题静态化+双向偏序统计·树状数组(高效统计元素大小关系·排序算法(预处理偏序和时间戳)·前缀和(合并单个贡献为总逆序对·动态问题静态化
学嵌入式的小杨同学15 小时前
【Linux 封神之路】信号编程全解析:从信号基础到 MP3 播放器实战(含核心 API 与避坑指南)
java·linux·c语言·开发语言·vscode·vim·ux
爱吃rabbit的mq16 小时前
第09章:随机森林:集成学习的威力
算法·随机森林·集成学习
精彩极了吧16 小时前
C语言基本语法-自定义类型:结构体&联合体&枚举
c语言·开发语言·枚举·结构体·内存对齐·位段·联合
(❁´◡`❁)Jimmy(❁´◡`❁)17 小时前
Exgcd 学习笔记
笔记·学习·算法
YYuCChi17 小时前
代码随想录算法训练营第三十七天 | 52.携带研究材料(卡码网)、518.零钱兑换||、377.组合总和IV、57.爬楼梯(卡码网)
算法·动态规划
不能隔夜的咖喱17 小时前
牛客网刷题(2)
java·开发语言·算法
VT.馒头17 小时前
【力扣】2721. 并行执行异步函数
前端·javascript·算法·leetcode·typescript