希尔排序详解

发布时间 2023-12-10 12:05:38作者: 帅帅的编程之路

在讲解希尔排序之前,我们有必要先回头看一下插入排序的问题。【插入排序学习】

插入排序不管数组分布是怎么样的,都是一步步的对元素进行比较,移动,插入。比如[5,4,3,2,1,0]这种倒序序列,数组末端的0要回到首位很费劲,比较和移动元素均需n-1次。这时就引出了希尔排序。

  希尔排序也是一种插入排序,它是简单插入排序经过改进之后的更高效的版本。该算法是突破O(n^2)的第一批算法之一。

  希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序。随着增量逐渐减小,每组包含的数字越来越多,当增量减至1时,整个文件恰好被分成一组,算法便终止。

图解希尔排序

  我们来看下希尔排序的基本步骤,我们选择增量gap = length/2,缩小增量继续以gap = gap/2 的方式,这种增量选择我们可以用一个序列来表示,{n/2,(n/2)/2...1}的增量序列。

  原始数组 以下数据元素颜色相同的为一组【gap:等距离差量值】

 初始增量 gap = length/2 = 5,所以整个数组被分为5组,即从第一位置的数字开始向后+n*gap为同一组,所以第一次增量后5组为[8,3],[9,5],[1,4],[7,6],[2,0]

  对这五组分别进行插入排序,例如[8,3]插入排序后会交换位置[3,8],[9,5]插入排序后会交换位置为[5,9]等等。所以执行后会将小的数字放在数组的前面。

  然后继续减小增量gap=5/2 = 2,数组被分为2组,即从第一位置的数字开始向后+n*gap为同一组,第二次增量后2组为[3,1,0,9,7] 和 [5,6,8,4,2]。如下图所示

对以上两组再分别进行插入排序,在[3,1,0,9,7]中会变为[0,1,3,7,9], [5,6,8,4,2]会变为[2,4,5,6,8],如下图所示,可以看到整个数组的有序程度更进一步了。

  再缩小增来gap=2/2 = 1 ,此时整个数组为1组[0,2,1,4,3,5,7,6,9,8],如下图所示

 代码实践

#include <stdio.h>
#define N 10

// 分组排序函数
void gapInsertSort(int arr[], int gap);

int main() {
    // 标准的输入输出不需要缓存,直接输出
    setbuf(stdout, NULL);
    int arr[N] = {8, 9, 1, 7, 2, 3, 5, 4, 6, 0};
    int gap = N / 2;
    while(gap >= 1) {
        gapInsertSort(arr, gap);
        gap = gap / 2;
    }
    printf("数组升序排列:");
    for (int i = 0; i < N; ++i) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    return 0;
}

// gap 等距离差量值
void gapInsertSort(int arr[], int gap) {
    for (int i = gap; i < N; ++i) {     // 无序区
        int temp = arr[i];
        int j = i - gap;
        while (j >= 0 && arr[j] > temp) {   // 有序区:每个有序的分组进行比较,移动,插入
            arr[j + gap] = arr[j];
            j = j - gap;
        }
        arr[j + gap] = temp;
    }
}

运行结果

数组升序排列:0 1 2 3 4 5 6 7 8 9 

总结:每天学习一点点,加强自身技能培养,越来越有自信,坚持!