| 项目 | 值 |
|---|---|
| 主题 | 插入排序 |
| 核心概念 | 将序列分为已排序/未排序两部分,每趟把未排序元素插入已排序部分 |
| 时间复杂度 | 直接插入:最好 $O(n)$ / 平均·最坏 $O(n^2)$;折半插入比较 $O(n\log_2 n)$、移动仍 $O(n^2)$ |
| 空间复杂度 | $O(1)$ |
| 稳定性 | ✅ 稳定 |
| 难度 | ⭐⭐ |
插入排序(Insertion Sort)是一种简单直观的排序算法,其核心思想类似于整理扑克牌:每次从待排序序列中取出一张牌,插入到已排序序列的正确位置。
void InsertSort(int A[], int n) {
int i, j, temp;
for (i = 1; i < n; i++) { // 从第二个元素开始
if (A[i] < A[i-1]) { // 如果当前元素比前一个小,需要插入
temp = A[i]; // 暂存当前元素
// 从后往前查找插入位置
for (j = i - 1; j >= 0 && A[j] > temp; j--)
A[j+1] = A[j]; // 元素后移
A[j+1] = temp; // 插入到正确位置
}
}
}
void BinaryInsertSort(int A[], int n) {
int i, j, low, high, mid, temp;
for (i = 1; i < n; i++) {
temp = A[i]; // 暂存当前元素
low = 0; high = i - 1;
// 二分查找插入位置
while (low <= high) {
mid = (low + high) / 2;
if (A[mid] > temp)
high = mid - 1;
else
low = mid + 1; // 相等时也往右找,保证稳定性
}
// 统一后移元素
for (j = i - 1; j >= high + 1; j--)
A[j+1] = A[j];
A[high+1] = temp; // 插入到正确位置
}
}
对序列 {49, 38, 65, 97, 76, 13, 27, 49*} 进行直接插入排序(* 表示与前面值相等但后出现的副本,用于检验稳定性):
初始: [49] 38 65 97 76 13 27 49*
↑已排序
第1趟: [38 49] 65 97 76 13 27 49* // 38 < 49, 38 插入到 49 前
↑已排序
第2趟: [38 49 65] 97 76 13 27 49* // 65 > 49, 位置不变
↑已排序
第3趟: [38 49 65 97] 76 13 27 49* // 97 > 65, 位置不变
↑已排序
第4趟: [38 49 65 76 97] 13 27 49* // 76 < 97, 插入到 65 和 97 之间
详细: 97 后移 → 65 不后移 → 76 放入
第5趟: [13 38 49 65 76 97] 27 49* // 13 最小,插入到最前面
详细: 97,76,65,49,38 依次后移 → 13 放入
第6趟: [13 27 38 49 65 76 97] 49* // 27 插入到 13 和 38 之间
详细: 97,76,65,49,38 依次后移 → 27 放入
第7趟: [13 27 38 49 49* 65 76 97] // 49* 插入到 49 之后(稳定性!)
详细: 97,76,65 依次后移 → 49* 放入 49 之后
最终结果:13, 27, 38, 49, 49*, 65, 76, 97(49 在 49* 前面,稳定!)
| 情况 | 直接插入排序 | 折半插入排序 |
|---|---|---|
| 最好情况 | $O(n)$ | $O(n\log_2 n)$ |
| 平均情况 | $O(n^2)$ | $O(n^2)$ |
| 最坏情况 | $O(n^2)$ | $O(n^2)$ |
空间复杂度为 $O(1)$
直接插入排序和折半插入排序都是稳定的排序算法。
原因:
A[j] == temp 时,循环条件 A[j] > temp 不满足,停止移动,temp 放在 A[j] 后面A[mid] == temp 时,low = mid + 1(往右找),保证相等元素的相对顺序不变A[j] > temp 不满足),新元素放在相等元素后面。(暂无关联知识点)