《画解数据结构》「希尔排序」算法教程

Posted 英雄哪里出来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《画解数据结构》「希尔排序」算法教程相关的知识,希望对你有一定的参考价值。

本文已收录于专栏
🌳《画解数据结构》🌳

零、📃前言

  「 希尔排序 」 是一种改进版的插入排序,比「 简单插入排序 」更加高效。在讲排序这块内容时,我会尽量做到「 深入浅出 」,让 90%「 零基础小白 」 也都能理解,真正做到 「让天下没有难学的算法」 。我知道这很难,但是我愿意尝试!我会尽量把文章写得有趣,一气呵成,不浪费读者的宝贵时间。毕竟,「 时间就是金钱 」

🔥让天下没有难学的算法🔥

C语言免费动漫教程,和我一起打卡!
🌞《光天化日学C语言》🌞

入门级C语言真题汇总
🧡《C语言入门100例》🧡

几张动图学会一种数据结构
🌳《画解数据结构》🌳

组团学习,抱团生长
🌌《算法入门指引》🌌

竞赛选手金典图文教程
💜《夜深人静写算法》💜

那么,我的教程和别人的教程有什么不同的地方呢?
  「第一步」简单释义: 我会简单解释一下这个算法的目的、思想、以及为什么叫这个名字以帮助记忆。
  「第二步」核心思想: 我会大致介绍一下这个算法的核心思想。
  「第三步」动图演示: 我会引入一个动图,并且用一个切实的例子展示一下算法执行的全过程。
  「第四步」算法前置: 在学习这个算法之前,我们需要学习的前置内容有哪些?这些内容是需要事先去攻克的。
  「第五步」算法描述: 细致的讲解整个算法的执行流程。
  「第六步」算法分析: 对算法的时间复杂度和空间复杂度进行一个详细的分析。
  「第七步」优化方案: 介绍一些可以优化的点。
  「第八步」代码实践: 用 C/C++ 来实现上述算法。
  「第九步」代码验证: 最后,我会推荐一些比较好用的在线评测系统来验证我们实现的算法的正确性。

一、🎯简单释义

1、算法目的

  将原本乱序的数组变成有序,可以是 「升序」 或者 「降序」 (为了描述统一,本文一律只讨论 「 升序」 的情况)。

2、算法思想

  「 希尔排序 」是把数据按下标的一定「 增量 」进行分组,对每组使用「 简单插入排序 」算法进行排序;随着「 增量 」逐渐减少,每组包含的数据越来越多,当「 增量 」减至 1 1 1 时,所有数据恰被分成一组,最后执行一次「 简单插入排序 」后,算法终止。

3、命名由来

  该算法是希尔(Donald Shell)于1959年提出的一种排序算法,故此命名 「 希尔排序 」

二、🧡核心思想

  • 「增量」:这里说的增量是数组下标的增量。
  • 「插入」:从后往前,寻找一个合适位置进行插入。
  • 「分组」:按照不同起点对待排序数据进行分组。

三、🔆动图演示

1、样例

856437102
  • 初始情况下的数据如 图二-1-1 所示,基本属于乱序,纯随机出来的数据。

图二-1-1

2、算法演示

  接下来,我们来看下排序过程的动画演示,总共分为三趟。如 图二-2-1 所示:

图二-2-1

  一下子看完不是很理解,没有关系,我们把这几个过程分拆开来。

3、动图分解

  第一趟分解后,如 图二-2-2 所示:

图二-2-2

  增量为 4,所有元素总共分为 4 组,分别为 [8, 3][5, 7][6, 10][4, 2],同组内部分别执行插入排序,得到 [3, 8][5, 7][6, 10][2, 4](由于每组只有两个元素,所以升序的情况位置不变,降序的情况执行组内元素位置交换,抖动一下代表保持原顺序不变,有一种 “我不换 ~~ 我不换” 的意思在里面 )。

  第二趟分解后,如 图二-2-3 所示:

图二-2-3

  增量为 2,所有元素总共分为 2 组,分别为 [3, 6, 8, 10][5, 2, 7, 4],同组内部分别执行插入排序, [3, 6, 8, 10]已经升序,保持原样; [5, 2, 7, 4] 执行三次插入排序后变成 [2, 4, 5, 7]
  第三趟分解后,如 图二-2-4 所示:

图二-2-4

  增量为 1,所有元素归为 1 组,为 [3, 2, 6, 4, 8, 5, 10, 7]。对它执行简单插入排序,执行完毕后,必然可以保证所有元素有序。

四、🌳算法前置

1、插入排序的实现

2、增量的实现
  这里的增量指的是下标增量,即下标满足一个等差数列关系。假设数据元素存储在数组 a[0 ... n-1]中,增量用变量 g a p gap gap 来表示,那么 a[i], a[i + gap], a[i + gap * 2], ... , a[i + gap * k]会被分到同一组。每次组内排序就是在这个增量数组中执行插入排序。

五、🥦算法描述

1、问题描述

  给定一个 n n n 个元素的整型数组,数组下标从 0 0 0 开始,采用「 希尔排序 」将数组按照 「升序」排列。

2、算法过程

整个算法的执行过程分以下几步:
  1) 定义增量 g a p = n / 2 , n / 4 , n / 8 , . . . , 1 gap = n/2,n/4,n/8,...,1 gap=n/2n/4n/8...1
  2) i ∈ [ g a p , n − 1 ] i \\in [gap, n-1] i[gap,n1],且需要执行插入的数 x = a [ i ] x = a[i] x=a[i]
  3) j ∈ [ i , g a p ] j \\in [i, gap] j[i,gap],执行步长为 g a p gap gap,将 a[i]插入到增量有序数组 a[i-k*gap], ...., a[i-2*gap], a[i-gap]中,注意原地算法需要逆序进行。


六、🧶算法分析

1、时间复杂度

  • 「 希尔排序 」的时间复杂度是 O ( n 1.3 − 2 ) O(n^{1.3-2}) O(n1.32)。时间复杂度为 O ( n l o g 2 n ) O(n log_2n) O(nlog2n) 的快速排序算法快 ,因此对中等大小规模表现良好,但对规模非常大的数据排序并不是最优选择。
  • 想要弄清数据比较次数和移动次数与增量选择之间的关系,并给出完整的数学分析,至今仍然是数学难题,所以我比较有自知之明,这块我就不瞎分析了。

2、空间复杂度

  • 由于不需要借助任何辅助数组,所以空间复杂度为 O ( 1 ) O(1) O(1)

七、🧢优化方案

  「 希尔排序 」 执行时间也依赖于增量序列。在「 希尔排序 」开始时增量较大,分组较多,每组的记录数目少,故各组内直接插入较快,后来随着增量逐渐缩小,分组数据逐渐减少,而各组的数据逐渐增多,但是由于之前几次排序,使数据较接近于有序状态,所以新的一趟排序过程也较快。
  所以,「 希尔排序 」的核心在于增量的选择,建议读者生成不同的增量序列来对各种海量数据进行排序尝试。

八、💙源码详解

#include <stdio.h>
#include <malloc.h>
 
#define maxn 1000001

int a[maxn];

void Input(int n, int *a) {
    for(int i = 0; i < n; ++i) {
        scanf("%d", &a[i]);
    }
}

void Output(int n, int *a) {
    for(int i = 0; i < n; ++i) {
        if(i)
            printf(" ");
        printf("%d", a[i]);
    }
    puts("");
}

void ShellSort(int n, int a[]){
    int i, j, tmp, gap;
    for(gap = n / 2; gap > 0; gap /= 2) {      // (1)   
        for(i = gap; i < n; ++i) {             // (2)   
            tmp = a[i];
            for(j = i; j >= gap; j -= gap) {   // (3)   
                if(tmp < a[j - gap]) {         // (4)    
                    a[j] = a[j - gap];
                }else {
                    break;                     // (5)  
                }
            }
            a[j] = tmp;                        // (6)  
        }
    }
}

int main() {
    int n;
    while(scanf("%d", &n) != EOF) {
        Input(n, a);
        ShellSort(n, a);
        Output(n, a);
    }
    return 0;
} 
  • ( 1 ) (1) (1) 每隔gap个数作为一组,gap = n/2, n/4, n/8, ..., 1
  • ( 2 ) (2) (2) 从第gap个数开始遍历;
  • ( 3 ) − ( 5 ) (3)-(5) (3)(5) 找到一个需要插入的位置,并且跳出循环;
  • ( 6 ) (6) (6) j j j 个位置就是这个数需要插入的位置 ;

九、💗代码验证

  • 比如,你可以在百度上搜索 代码在线提交OnlineJudgeLeetCode洛谷HDOJPOJ 等等的关键词,然后去找对应的题目提交验证你的代码的正确性。

  • 关于 「 希尔排序 」 的内容到这里就结束了。
  • 如果还有不懂的问题,可以通过 「 作者主页 」(电脑版)找到作者的「 联系方式 」 进行在线咨询。


🔥让天下没有难学的算法🔥

C语言免费动漫教程,和我一起打卡!
🌞《光天化日学C语言》🌞

入门级C语言真题汇总
🧡《C语言入门100例》🧡

几张动图学会一种数据结构
🌳《画解数据结构》🌳

组团学习,抱团生长
🌌《算法入门指引》🌌

竞赛选手金典图文教程
💜《夜深人静写算法》💜

以上是关于《画解数据结构》「希尔排序」算法教程的主要内容,如果未能解决你的问题,请参考以下文章

《画解数据结构》「快速排序」算法教程

❤️五万字《十大排序算法》动图讲解❤️(建议收藏)

❤️五万字《十大排序算法》动图讲解❤️(建议收藏)

万字总结画解八大排序算法

❤️六万字《算法和数据结构》之《画解数据结构》总纲,算法零基础教程❤️(建议收藏)

❤️六万字《算法和数据结构》之《画解数据结构》总纲,算法零基础教程❤️(建议收藏)