【初探数据结构】直接插入排序与希尔排序详解

发布于:2025-03-25 ⋅ 阅读:(34) ⋅ 点赞:(0)

💬 欢迎讨论:在阅读过程中有任何疑问,欢迎在评论区留言,我们一起交流学习!
👍 点赞、收藏与分享:如果你觉得这篇文章对你有帮助,记得点赞、收藏,并分享给更多对数据结构感兴趣的朋友

文章目录

    • 1. 直接插入排序
      • 算法原理
      • 代码解析
      • 时间复杂度
      • 优缺点
    • 2. 希尔排序
      • 算法原理(预排序+插入排序)
      • 代码解析
      • 时间复杂度
      • 优缺点
    • 3. 对比与适用场景
    • 4. 测试与示例
      • 测试代码
    • 5. 总结


1. 直接插入排序

算法原理

插入排序是一种简单直观的排序算法,核心思想是:将数组分为“已排序”和“未排序”两部分。每次从未排序部分取出第一个元素,将其插入到已排序部分的正确位置,直到所有元素有序。

实际中我们玩扑克牌时,就用了插入排序的思想
在这里插入图片描述

步骤

  1. 从第二个元素开始(假设第一个元素已排序)。
  2. 将当前元素与已排序部分的元素从后向前比较。
  3. 若当前元素更小,则将已排序元素后移,直到找到合适位置插入。

这个图可以帮助我们更好地理解这个逻辑:
在这里插入图片描述

代码解析

void InsertSort(int* a, int n) {
	//从第二个元素开始
    for (int i = 1; i < n; i++) {
        int end = i - 1;
        int tmp = a[i];
        //2.依次比较
        while (end >= 0) {
            if (tmp < a[end]) {
                a[end + 1] = a[end];
                end--;
            } else break;
        }
        //3.插入
        a[end + 1] = tmp;
    }
}
  • 外层循环:从 i=1 第二个元素开始遍历所有元素。
  • 内层循环:将 a[i] 插入到已排序区间 [0, i-1] 的合适位置。

时间复杂度

  • 最好情况(已有序):O(n)
  • 最坏/平均情况O(n²)

优缺点

  • 优点:简单、稳定,适合小数据或部分有序数据。
  • 缺点:大规模数据效率低。

2. 希尔排序

希尔排序是对直接插入排序的优化

算法原理(预排序+插入排序)

希尔排序是插入排序的改进版,通过“分组插入排序”减少元素移动次数。其核心思想是:将数组按间隔 gap 分组,对每组进行插入排序,随后逐步缩小 gap 直至为 1,最终进行一次全数组插入排序。

在这里插入图片描述
gap > 1时都是预排序,目的是让数组更接近于有序。当gap == 1时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。

代码解析

void ShellSort(int* a, int n) {
    int gap = n;
    while (gap > 1) {
        gap = gap / 3 + 1;  // 间隔序列
        for (int i = gap; i < n; i++) {
            int end = i - gap;
            int tmp = a[i];
            while (end >= 0) {
                if (tmp < a[end]) {
                    a[end + gap] = a[end];
                    end -= gap;
                } else break;
            }
            a[end + gap] = tmp;
        }
    }
}
  • 间隔序列gap = gap / 3 + 1 确保最终 gap=1,进行最后一次完整插入排序。
  • 分组插入:每组元素间隔为 gap,通过插入排序调整位置。

时间复杂度

希尔排序的时间复杂度不好计算,因为gap的取值方法很多,导致很难去计算,因此在好多书中给出的希尔排序的时间复杂度都是不固定的。

大多数参考资料提出:

  • 平均情况O(n^1.3)(取决于间隔序列)
  • 最坏情况O(n²)

优缺点

  • 优点:比插入排序高效,适合中等规模数据。
  • 缺点:不稳定,间隔序列的选择影响性能。

3. 对比与适用场景

特性 插入排序 希尔排序
时间复杂度 O(n²) O(n^1.3) ~ O(n²)
空间 O(1) O(1)
适用场景 小数据或部分有序 中等规模数据

4. 测试与示例

测试代码

// 插入排序测试
void TestInsertSort() {
    int a[] = {9,8,7,6,5,4,3,2,1,0};
    InsertSort(a, sizeof(a)/sizeof(int));
    PrintSort(a, sizeof(a)/sizeof(int)); 
    // 输出:0 1 2 3 4 5 6 7 8 9
}

// 希尔排序测试
void TestShellSort() {
    int a[] = {9,8,7,6,5,4,3,2,1,0,-1,-2,-3,-4,-5,-6,-7,-8,-9};
    ShellSort(a, sizeof(a)/sizeof(int));
    PrintSort(a, sizeof(a)/sizeof(int)); 
    // 输出:-9 -8 -7 ... 7 8 9
}

5. 总结

  • 插入排序:简单但低效,适合小规模数据。
  • 希尔排序:通过分组优化插入排序,性能显著提升,是插入排序的高效变种。
  • 选择建议:数据规模较小时用插入排序,中等规模用希尔排序,大规模数据可考虑更高效的算法(如快排、归并)后序会一一讲解。