当前位置: 首页 > news >正文

插入排序(直接插入排序、折半插入排序和希尔排序)

直接插入排序、折半插入排序和希尔排序都属于插入排序类算法,它们的核心思想都是将待排序元素逐步插入到已排序序列的合适位置,但在具体实现细节上存在差异,下面分别进行介绍:

直接插入排序

  • 基本思想
    将待排序的记录按其关键码值的大小,逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。
  • 算法步骤
    1. 从第一个元素开始,该元素可以认为已经被排序。
    2. 取出下一个元素,在已经排序的元素序列中从后向前扫描。
    3. 如果该元素(已排序)大于新元素,将该元素移到下一位置。
    4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置。
    5. 将新元素插入到该位置后。
    6. 重复步骤2~5,直到所有元素均排序完毕。
  • 示例
    对序列[5, 2, 4, 6, 1, 3]进行直接插入排序:
    • 初始有序序列为[5],未排序序列为[2, 4, 6, 1, 3]
    • 插入2[2, 5],未排序序列为[4, 6, 1, 3]
    • 插入4[2, 4, 5],未排序序列为[6, 1, 3]
    • 插入6[2, 4, 5, 6],未排序序列为[1, 3]
    • 插入1[1, 2, 4, 5, 6],未排序序列为[3]
    • 插入3[1, 2, 3, 4, 5, 6],排序完成。
  • 时间复杂度
    • 最好情况(序列已有序): O ( n ) O(n) O(n),此时只需进行 n − 1 n - 1 n1次比较,不需要移动元素。
    • 最坏情况(序列逆序): O ( n 2 ) O(n^2) O(n2),需要进行 n ( n − 1 ) 2 \frac{n(n - 1)}{2} 2n(n1)次比较和移动。
    • 平均情况: O ( n 2 ) O(n^2) O(n2)
  • 代码实现
//A[0]为哨兵,n:元素个数
void InsertSort(Element A[], int n) {for (int i = 2; i <= n; i++) {if (A[i] < A[i - 1]) {A[0] = A[i];int k = i;while (k > 1 && A[0] < A[k - 1]) {//找到待插入位置A[k] = A[k - 1];k--;}A[k] = A[0];//插入元素}}
}
  • 空间复杂度
    O ( 1 ) O(1) O(1),是一种稳定的排序算法,仅需要常数个额外空间用于临时存储。
  • 特点
    实现简单,对于小规模数据或基本有序的数据效率较高,但在大规模无序数据排序时效率较低。

折半插入排序

  • 基本思想
    折半插入排序是对直接插入排序的改进,直接插入排序在寻找插入位置时采用顺序查找的方式,而折半插入排序采用折半查找(二分查找)的方式确定插入位置,以减少比较次数。
  • 算法步骤
    1. 将第一个元素视为已排序序列。
    2. 对于未排序序列中的每个元素,使用折半查找在已排序序列中找到合适的插入位置。
    3. 将插入位置及之后的元素依次后移一位。
    4. 将当前元素插入到找到的位置。
    5. 重复步骤2~4,直到所有元素均排序完毕。
  • 示例
    对序列[5, 2, 4, 6, 1, 3]进行折半插入排序:
    • 初始有序序列为[5],未排序序列为[2, 4, 6, 1, 3]
    • 插入2:通过折半查找确定插入位置为0,得到[2, 5],未排序序列为[4, 6, 1, 3]
    • 插入4:通过折半查找确定插入位置为1,得到[2, 4, 5],未排序序列为[6, 1, 3]
    • 插入6:通过折半查找确定插入位置为3,得到[2, 4, 5, 6],未排序序列为[1, 3]
    • 插入1:通过折半查找确定插入位置为0,得到[1, 2, 4, 5, 6],未排序序列为[3]
    • 插入3:通过折半查找确定插入位置为2,得到[1, 2, 3, 4, 5, 6],排序完成。
  • 代码实现

void InsertSort(Element A[], int n) {for (int i = 2; i <= n; i++) {if (A[i] < A[i-1]) {A[0] = A[i]; // 哨兵,暂存待插入元素//通过折半查找找到待插入位置,最终left即为待插入位置int left = 1, right = i -1;while (left <= right) {int mid = (left + right) / 2;if (A[mid] <= A[0]) {left= mid + 1;} else {right = mid - 1;}}//移动元素for (int k = i; k > left; k--) {A[k] = A[k - 1];}A[left] = A[0];//插入元素}}
}
  • 时间复杂度
    • 最好情况(序列已有序): O ( n ) O(n) O(n),比较次数为 ⌈ log ⁡ 2 n ! ⌉ ≈ n log ⁡ 2 n \lceil\log_2n!\rceil\approx n\log_2n log2n!⌉nlog2n,但移动元素次数仍为 O ( n ) O(n) O(n)
    • 最坏情况(序列逆序): O ( n 2 ) O(n^2) O(n2),比较次数为 n ( n − 1 ) 4 \frac{n(n - 1)}{4} 4n(n1),移动元素次数为 n ( n − 1 ) 2 \frac{n(n - 1)}{2} 2n(n1)
    • 平均情况: O ( n 2 ) O(n^2) O(n2),但比较次数比直接插入排序少。
  • 空间复杂度
    O ( 1 ) O(1) O(1),是稳定的排序算法,仅需常数个额外空间。
  • 特点
    减少了比较次数,提高了排序效率,但移动元素的次数与直接插入排序相同,对于大规模数据排序效率提升有限。

希尔排序

  • 基本思想
    希尔排序是插入排序的一种更高效的改进版本,也称为缩小增量排序。它先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行一次直接插入排序。
  • 算法步骤
    1. 选择一个增量序列 t 1 , t 2 , ⋯ , t k t_1, t_2, \cdots, t_k t1,t2,,tk,其中 t i > t i + 1 t_i > t_{i + 1} ti>ti+1 t k = 1 t_k = 1 tk=1
    2. 按增量序列个数 k k k,对序列进行 k k k趟排序。
    3. 每趟排序,根据对应的增量 t i t_i ti,将待排序列分割成若干长度为 m m m的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 1 1时,整个序列作为一个表来处理,表长度即为整个序列的长度。
  • 示例
    对序列[9, 1, 5, 8, 3, 7, 4, 6, 2]进行希尔排序,选择增量序列 5 , 2 , 1 5, 2, 1 5,2,1
    • 第一趟排序(增量 d = 5 d = 5 d=5
      将序列分成 5 5 5个子序列:[9, 3][1, 7][5, 4][8, 6][2],分别对这 5 5 5个子序列进行直接插入排序,得到[3, 1, 4, 6, 2, 9, 7, 8, 5]
    • 第二趟排序(增量 d = 2 d = 2 d=2
      将序列分成 2 2 2个子序列:[3, 4, 2, 7, 5][1, 6, 9, 8],分别对这两个子序列进行直接插入排序,得到[2, 1, 3, 6, 4, 5, 7, 8, 9]
    • 第三趟排序(增量 d = 1 d = 1 d=1
      对整个序列进行直接插入排序,得到最终有序序列[1, 2, 3, 4, 5, 6, 7, 8, 9]
  • 时间复杂度
    希尔排序的时间复杂度取决于增量序列的选择,最坏情况下时间复杂度为 O ( n 2 ) O(n^2) O(n2),但通过选择合适的增量序列,复杂度可达到 O ( n 3 2 ) O(n^{\frac{3}{2}}) O(n23)
  • 空间复杂度
    O ( 1 ) O(1) O(1),是不稳定的排序算法,因为分组插入排序可能导致相同元素的相对位置发生改变。
  • 特点
    希尔排序通过分组插入排序,使得数据在大范围内先“宏观有序”,再在小范围内“微观有序”,大大提高了排序效率,尤其适用于大规模数据排序。

总结

  • 直接插入排序:实现简单,适合小规模或基本有序的数据,但大规模无序数据排序效率低。
  • 折半插入排序:在直接插入排序基础上减少比较次数,但移动元素次数不变,效率提升有限。
  • 希尔排序:通过分组插入排序显著提高大规模数据排序效率,是不稳定排序中性能较好的代表。

相关文章:

  • C++中析构函数
  • log4cpp进阶指南
  • LeetCode 每日一题 2025/4/21-2025/4/27
  • 关于Spark知识点与代码测试的学习总结
  • element-ui dropdown 组件源码分享
  • 【c++】AVL树模拟实现
  • Comfy UI 笔记
  • 文章记单词 | 第47篇(六级)
  • 面试记录1-春招补录0427
  • 基础学习:(9)vit -- vision transformer 和其变体调研
  • 《大型网站技术架构-核心原理与案例分析》笔记
  • UV工具的安装与使用
  • Leetcode:283. 移动零
  • Scala 函数柯里化及闭包
  • 343. 整数拆分
  • Tailwind CSS 实战:基于 Kooboo 构建企业官网页面(二)
  • 【SF】在 Android 显示系统中,图层合成方式 Device 和 Client 的区别
  • 信创系统资产清单采集脚本:主机名+IP+MAC 一键生成 CSV
  • 汽车产业链主表及类别表设计
  • 2、Linux操作系统下,ubuntu22.04版本安装搜狗输入法
  • 借助AI应用,自闭症人群开始有可能真正“读懂他人”
  • 第1现场|无军用物资!伊朗港口爆炸已遇难40人伤1200人
  • 美媒:受关税政策影响,美国电商平台近千种商品平均涨价29%
  • 清华姚班,正走出一支军团
  • 公交公司须关注新出行需求:“单车巴士”能否常态化
  • 广州一人均500元的日料店回收食材给下一桌?市场监管部门介入调查