八大排序之插入排序+希尔排序

一、插入排序(基础必懂)

1. 核心思想(打扑克理牌法)

把数组一刀切成两部分

  • 左边:已排序区(初始只有第一个元素)
  • 右边:未排序区(剩下所有元素)

核心动作 :每次从未排序区的第一个元素 拿出来,从后往前和已排序区的元素逐个比较,找到它应该在的位置,插入进去。直到未排序区为空。

2. 完整算法步骤

  1. 从下标i=1开始遍历数组(因为第一个元素默认已排序)
  2. 取出当前元素temp = arr[i](保存要插入的元素,防止被覆盖)
  3. 从已排序区的末尾j=i-1开始,从后往前 比较:
    • 如果arr[j] > temp:把arr[j]向后移动一位(arr[j+1] = arr[j]),j--
    • 如果arr[j] <= temp:找到插入位置,跳出循环
  4. temp插入到j+1的位置
  5. 重复步骤 2-4,直到i遍历完整个数组

3. 结合示例数组的逐元素详细过程

示例数组:[8, 9, 1, 7, 2, 3, 5, 4, 6, 0](长度 10,共 9 趟)

我挑 ** 最复杂的第 9 趟(处理最后一个元素 0)** 给你拆解每一步操作:

  • 此时已排序区:[1, 2, 3, 4, 5, 6, 7, 8, 9]
  • 未排序区第一个元素:0(下标i=9
  • 步骤 1:保存temp = 0
  • 步骤 2:j=8(已排序区最后一个元素下标)
    • 比较arr[8]=9 > 0 → 9 后移到下标 9 → 数组变为[1,2,3,4,5,6,7,8,9,9]j=7
    • 比较arr[7]=8 > 0 → 8 后移到下标 8 → 数组变为[1,2,3,4,5,6,7,8,8,9]j=6
    • 比较arr[6]=7 > 0 → 7 后移到下标 7 → 数组变为[1,2,3,4,5,6,7,7,8,9]j=5
    • ...(以此类推,所有比 0 大的元素都向后移一位)
    • 最终j=-1(已排序区所有元素都比 0 大)
  • 步骤 3:把temp=0插入到j+1=0的位置
  • 最终数组:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

4. 关键特性

  • 时间复杂度:最好 O (n)(数组已经有序),最坏 O (n²)(数组完全逆序),平均 O (n²)
  • 空间复杂度:O (1)(原地排序,只需要一个临时变量)
  • 稳定性稳定排序(相等元素的相对顺序不变)
  • 优点 :实现简单,对基本有序的数组效率极高
  • 缺点:对逆序数组效率极低(每个元素都要移动到最前面)
cpp 复制代码
void insertSort(int* arr, int n)
{
	for (int i = 1; i < n; i++)
	{
		int temp = arr[i];
		int j = i - 1;
		for ( ;j >= 0; j--)
		{
			if (arr[j] > temp)
			{
				arr[j + 1] = arr[j];
			}
			else
			{
				
				break;
			}
			
		}
		arr[j + 1] = temp;
	}
}

二、希尔排序(插入排序的 "超级改良版")

1. 为什么需要希尔排序?

插入排序有一个致命弱点:如果数组是完全逆序的,每个元素都要移动到最前面,时间复杂度直接拉满到 O (n²)。

希尔排序的天才想法:先让数组 "基本有序",再用插入排序收尾。这样最后一步插入排序的时间复杂度就会接近最好情况 O (n)。

2. 核心思想(分组预排序法)

  1. 分组 :选择一个增量 gap ,把整个数组分成gap个小组(下标相差 gap 的元素为一组
  2. 组内插排:对每个小组分别进行插入排序
  3. 缩小增量:把 gap 减半(或按其他规则缩小),重复步骤 1-2
  4. 最终插排 :当gap=1时,整个数组变成一个小组,此时就是普通插入排序

3. 增量序列的选择

最常用、最简单的是希尔原始增量序列

  • 初始gap = n/2(向下取整)
  • 每次gap = gap/2
  • 直到gap=1

(面试常考:还有其他更优的增量序列,比如 Knuth 序列:gap = 3*gap + 1,但原始增量最容易理解和实现)

4. 结合示例数组的逐 gap 详细过程

示例数组:[8, 9, 1, 7, 2, 3, 5, 4, 6, 0](长度 n=10)增量序列:gap=5 → gap=2 → gap=1


第 1 趟:gap=5(分 5 个小组,每组 2 个元素)

分组规则:下标差 5 的元素为一组

  • 第 1 组:下标 0 和 5 → 元素83
  • 第 2 组:下标 1 和 6 → 元素95
  • 第 3 组:下标 2 和 7 → 元素14
  • 第 4 组:下标 3 和 8 → 元素76
  • 第 5 组:下标 4 和 9 → 元素20

每组内部插入排序

  • 第 1 组:3 < 8 → 交换 → 变为[3, 8]
  • 第 2 组:5 < 9 → 交换 → 变为[5, 9]
  • 第 3 组:4 > 1 → 不变 → 还是[1, 4]
  • 第 4 组:6 < 7 → 交换 → 变为[6, 7]
  • 第 5 组:0 < 2 → 交换 → 变为[0, 2]

第 1 趟结束后的数组[3, 5, 1, 6, 0, 8, 9, 4, 7, 2]


第 2 趟:gap=2(分 2 个小组,每组 5 个元素)

分组规则:下标差 2 的元素为一组

  • 第 1 组(偶数下标):下标 0,2,4,6,8 → 元素3, 1, 0, 9, 7
  • 第 2 组(奇数下标):下标 1,3,5,7,9 → 元素5, 6, 8, 4, 2

第 1 组内部插入排序(逐个处理组内第 2 到第 5 个元素):

  • 处理1(下标 2):比3小 → 插入到最前 → 组变为[1, 3, 0, 9, 7]
  • 处理0(下标 4):比1,3都小 → 插入到最前 → 组变为[0, 1, 3, 9, 7]
  • 处理9(下标 6):比3大 → 不变 → 组还是[0, 1, 3, 9, 7]
  • 处理7(下标 8):比9小 → 插入到9前面 → 组变为[0, 1, 3, 7, 9]

第 2 组内部插入排序

  • 处理6(下标 3):比5大 → 不变 → 组变为[5, 6, 8, 4, 2]
  • 处理8(下标 5):比6大 → 不变 → 组还是[5, 6, 8, 4, 2]
  • 处理4(下标 7):比5,6,8都小 → 插入到最前 → 组变为[4, 5, 6, 8, 2]
  • 处理2(下标 9):比4,5,6,8都小 → 插入到最前 → 组变为[2, 4, 5, 6, 8]

第 2 趟结束后的数组 (把两个组的元素按原下标顺序放回):[0, 2, 1, 4, 3, 5, 7, 6, 9, 8]


第 3 趟:gap=1(整个数组一个小组,普通插入排序)

此时数组已经基本有序,插入排序只需要很少的比较和移动就能完成:

  • 处理1(下标 2):插入到02之间 → [0,1,2,4,3,5,7,6,9,8]
  • 处理3(下标 4):插入到24之间 → [0,1,2,3,4,5,7,6,9,8]
  • 处理6(下标 7):插入到57之间 → [0,1,2,3,4,5,6,7,9,8]
  • 处理8(下标 9):插入到79之间 → [0,1,2,3,4,5,6,7,8,9]

最终排序完成

5. 关键特性

  • 时间复杂度:最好 O (n),最坏 O (n²)(原始增量),平均 O (n^1.3)(远优于插入排序)
  • 空间复杂度:O (1)(原地排序)
  • 稳定性不稳定排序(分组预排序会打乱相等元素的相对顺序)
  • 优点:对中等规模数组效率很高,比插入排序快得多
  • 缺点:实现比插入排序复杂,增量序列的选择会影响效率
cpp 复制代码
void ShellSort(int* arr, int len)
{
	for (int gap = len / 2; gap > 0; gap /= 2)
	{
		for (int i = gap; i < len; i++)
		{
			int temp = arr[i];
			int j = i - gap;
			for (; j >= 0 && arr[j] > temp; j -= gap)
			{
				arr[j+gap] = arr[j];
			}
			arr[j + gap] = temp;
		}
	}
}

三、面试必考点对比

表格

特性 插入排序 希尔排序
核心思想 逐个插入已排序区 分组预排序 + 最终插排
时间复杂度(平均) O(n²) O(n^1.3)
稳定性 稳定 不稳定
适用场景 小规模、基本有序数组 中等规模数组
实现难度 简单 中等
相关推荐
To_OC7 小时前
LC 131 分割回文串:刚学回溯时,我连怎么切字符串都想不明白
javascript·算法·leetcode
旖-旎8 小时前
LeetCode 518:零钱兑换||(完全背包)—— 题解
c++·算法·leetcode·动态规划·背包问题
To_OC9 小时前
LC 42 接雨水:暴力超时卡半天?前后缀数组一用就通了
javascript·算法·leetcode
delishcomcn10 小时前
AI视觉识别+分切算法:电化铝缺陷检测与裁切一体化解锁
人工智能·算法
触底反弹10 小时前
深入理解大模型采样:Temperature、Top-K、Top-P 的原理与实战
人工智能·算法·面试
雪碧聊技术10 小时前
力扣 LCR 091. 粉刷房子 —— 动态规划入门详解
算法·动态规划
延凡科技12 小时前
多场景落地复盘:端边云架构无人机智能巡检系统设计与实践
大数据·数据结构·人工智能·科技·架构·无人机·能源
CV-Climber13 小时前
检索技术的实际应用
人工智能·算法
hhzz13 小时前
Tiger AI Platform平台中增加人脸识别功能
图像处理·人工智能·算法·计算机视觉·大模型