【C】初阶数据结构10 -- 希尔排序

本篇文章主要是讲解直接插入排序的优化算法 -- 希尔排序算法。

目录

[1 直接插入排序算法的优化](#1 直接插入排序算法的优化)

[2 算法思想](#2 算法思想)

[3 代码](#3 代码)

[4 时间复杂度与空间复杂度分析](#4 时间复杂度与空间复杂度分析)

[1) 时间复杂度](#1) 时间复杂度)

[2) 空间复杂度](#2) 空间复杂度)


1 直接插入排序算法的优化

在上一篇文章中,我们讲解了直接插入排序,可以了解到直接插入排序的时间复杂度是 O(n^2) 的,时间复杂度较高,所以在 1959 年,唐纳德.L.希尔(Donald.L.Shell)提出了直接插入排序算法的优化算法 -- 希尔排序算法。

直接插入排序是将未排好序的元素直接插入到已经排好序的序列之中,所以会出现大量的数据比较与移动,所以直接插入排序的大部分的时间耗费都是来自于元素的移动,当移动的元素次数越少时,其时间的耗费就会减少,时间复杂度也就会降低 。由此可见,当数组中的元素越接近有序,直接插入排序的时间耗费的越低的。比如:有两个序列(1)1,3,2,5,4和(2)5,4,3,2,1,排升序的话肯定是序列(1)的时间耗费更少。


2 算法思想

希尔排序就是利用了上面的优化方法,先将原来的数组按照一定的间隔分组,再将每组的数据进行直接插入排序,然后将间隔变小,再分组,再排序,最后直到间隔变为1的时候,数组基本就有序了,然后就变成了直接插入排序,希尔排序的过程如图所示:

首先设置每组的间隔(gap)为3 ,然后对整个数组中的数据进行分组,下标为0,3,6,9的数据为一组,下标为1,4,7的数据为一组,下标为2,5,8的数据为一组,对每组分别进行直接插入排序;之后再将间隔(gap)变为2 ,然后下标为0,2,4,6,8的数据为一组,下标为1,3,5,7,9的数据为一组,然后再对每一组进行直接插入排序;经过两次分组的排序之后,数组基本就接近有序了,再将间隔(gap)变为1,这样就变成了之前的直接插入排序,时间复杂度会大大降低。

希尔排序的关键就是 gap 的确定 ,gap 的大小确定了比较的次数以及交换的次数,假设数组长度为n,gap一般都确定为 n/3 + 1,或者是n/2 + 1,最后加1是为了防止gap越除越小的时候除到0,让其最后一次的gap变为1,以最初的直接插入排序结尾


3 代码

cpp 复制代码
//交换函数
void Swap(int* x, int* y)
{
  int tmp = *x;
  *x = *y;
  *y = tmp;
}

//希尔排序
void ShellSort(int* arr, int n)
{
  int gap = n;
  while (gap > 1)
  {
    gap = gap / 3 + 1;//这里的间隔除2,除3都可以,但是最后必须加个1
    //接下来就类似于直接插入排序
    for (int i = 0;i < n - gap; i++)
    {
      int end = i;
      int tmp = arr[end + gap];
      while (end >= 0)
      {
        Swap(&arr[end], &arr[end + gap]);
        end -= gap;
      }
      else
      {
        break;
      }
      arr[end + gap] = tmp;
    }
  }
}

解释 :这里的希尔排序的实现代码并不是对数据进行分组,依次对每组进行排序,而是对每一个数据跟同组的相邻数据进行比较,然后交换,例如: 当gap为3的时候,先对0下标的数据和3下标的数据进行比较和交换,然后下一次并不是对3下标和6下标的数据进行比较和交换,而是对1下标和4下标的数据进行比较和交换;依次进行下去,直到 n - 1 - gap下标的元素和 n - 1 下标的元素比较和交换完后(n为数组中元素的个数),当前 gap 为 3 的分组的比较就结束了;之后再将 gap 变为gap/3 + 1,继续执行上述过程,直到 gap 变为1,执行完一次直接插入排序之后,整个的希尔排序就结束了。

其实希尔排序相比于直接插入排序来说就只是增加了一个 gap 的循环,里面的比较逻辑变为了和同组的相邻元素之间的比较,其余的逻辑都跟直接插入排序是相同的,所以学懂了直接插入排序,希尔排序也会很容易学懂的。

测试用例:

cpp 复制代码
//打印函数
void Print(int* arr, int n)
{
    for (int i = 0; i < n; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

//测试用例
int main()
{
    int arr[] = { 10, 2, 5, 7, 1, 4, 8, 9, 6};
    int n = sizeof(arr) / sizeof(arr[0]);

    ShellSort(arr, n);
    Print(arr, n);
    return 0;
}

4 时间复杂度与空间复杂度分析

1) 时间复杂度

希尔排序的时间复杂度是不确定的,根据gap的不同,时间复杂度也是不一样的,但在平均情况下,时间复杂度是T(n) = O(n^1.3),基本上是接近 T(n) = O(nlogn)的。

2) 空间复杂度

由于希尔排序算法仅仅用了几个有限的变量,并不会随着空间规模的增大而使得使用空间增大,所以空间复杂度是 O(1) 的。

相关推荐
_GR20 分钟前
2025年蓝桥杯第十六届C&C++大学B组真题及代码
c语言·数据结构·c++·算法·贪心算法·蓝桥杯·动态规划
shinelord明2 小时前
【软件系统架构】事件驱动架构
数据结构·设计模式·架构·系统架构·软件工程
照海19Gin2 小时前
数据结构中的宝藏秘籍之广义表
c语言·数据结构·算法
加点油。。。。2 小时前
C语言高频面试题——strcpy与memcpy区别
c语言·开发语言
大炮筒2 小时前
CPPlist初识
数据结构·c++·list
浅浅2802 小时前
numpy、pandas内存优化操作整理
数据结构·经验分享·python·学习·性能优化·numpy·pandas
bloxd yzh2 小时前
筛选法(埃氏筛法)C++
数据结构·算法
双叶8363 小时前
(51单片机)LCD显示数据存储(DS1302时钟模块教学)(LCD1602教程)(独立按键教程)(延时函数教程)(I2C总线认识)(AT24C02认识)
c语言·数据库·单片机·嵌入式硬件·mongodb·51单片机·nosql
那就摆吧3 小时前
数据结构-栈
android·java·c语言·数据结构