首页/数据结构/07-sorting/折半插入排序 🔗 在 Obsidian 中打开
数据结构 · 07-sorting

折半插入排序

难度 ★★重要度 ★★★ 考查频率 中题型 算法 / 综合应用 数据结构/排序插入排序二分查找
速查
折半查找在已排序子序列中确定插入位置,比较次数从 $O(n^2)$ 降到 $O(n\log n)$;但移动次数不变,所以总时间仍为 $O(n^2)$稳定、原地,只适用于顺序表。

速查

项目
核心思想用折半查找确定插入位置,减少比较次数
最好时间$O(n\log n)$(比较次数)
最坏时间$O(n^2)$(移动次数不变)
平均时间$O(n^2)$(移动仍是瓶颈)
空间$O(1)$
稳定性✅ 稳定
改进点减少比较次数,不减少移动次数

核心概念

折半插入排序是直接插入排序的改进版本。它利用已排序子序列的有序性,用折半查找(二分查找)代替逐个比较来确定插入位置。

核心改进:直接插入排序在已排序部分从后往前逐个比较找位置($O(n)$ 次比较),折半插入排序用二分查找找位置($O(\log n)$ 次比较)。

重点折半查找只减少了比较次数移动次数没有改变(仍需逐个后移),所以整体时间复杂度仍为 $O(n^2)$。

代码实现

void BinaryInsertSort(int A[], int n) {
    int i, j, low, high, mid;
    for (i = 2; i <= n; i++) {
        A[0] = A[i];               // 暂存待插入元素
        low = 1; high = i - 1;     // 在已排序部分折半查找
        while (low <= high) {      // 找插入位置
            mid = (low + high) / 2;
            if (A[mid] > A[0]) high = mid - 1;
            else low = mid + 1;
        }
        for (j = i-1; j >= high+1; j--)  // 后移
            A[j+1] = A[j];
        A[high+1] = A[0];          // 插入
    }
}

手算示例

49, 38, 65, 97, 76, 13, 27, 49* 排序:

i=2: 待插入38,已排序[49]
  折半查找:low=1, high=1, mid=1, A[1]=49>38 → high=0
  插入位置:high+1=1,38插入位置1
  结果:[38 49] 65 97 76 13 27 49*

i=3: 待插入65,已排序[38, 49]
  折半查找:low=1, high=2, mid=1, A[1]=38<65 → low=2
  mid=2, A[2]=49<65 → low=3, low>high停止
  插入位置:high+1=3(原位置不变)
  结果:[38 49 65] 97 76 13 27 49*

i=5: 待插入76,已排序[38, 49, 65, 97]
  折半查找:找到插入位置在65和97之间
  结果:[38 49 65 76 97] 13 27 49*

关键性质

性质说明
比较次数$O(n\log n)$,每轮折半查找 $O(\log i)$
移动次数$O(n^2)$,与直接插入相同
总时间$O(n^2)$,移动是瓶颈
空间$O(1)$
稳定性稳定

与直接插入排序的比较

比较项直接插入折半插入
比较次数$O(n^2)$$O(n\log n)$
移动次数$O(n^2)$$O(n^2)$
总时间$O(n^2)$$O(n^2)$
稳定性稳定稳定

常见考法

考法 1:手动模拟折半查找过程写出每轮 low / high / mid 及比较结果。
考法 2:比较次数计算每轮 $\lfloor\log_2 i\rfloor+1$ 次比较,总和约 $n\log_2 n$。
考法 3:与直接插入的区别只减少比较次数,不减少移动次数。
考法 4:稳定性判断稳定(插入到 high+1 位置,相等元素在后面)。
考法 5:是否可能总时间变 $O(n\log n)$不可能,移动仍是 $O(n^2)$。

易错点

注意
  1. ⚠️ 折半插入排序仍然是 $O(n^2)$!只优化了比较,没优化移动。
  2. ⚠️ 折半查找找的是插入位置,找到相等元素时要继续向右(high+1)。
  3. ⚠️ 稳定性:插入到 high+1 位置,相等元素后移,相对位置不变 → 稳定。
  4. ⚠️ 只适用于顺序表(需要随机访问做折半查找),不适用于链表。
  5. ⚠️ 折半查找减少了比较次数,但比较不是排序的主要瓶颈(移动才是)。

核心结论

  1. 折半插入排序是对直接插入排序的改进,用折半查找减少比较次数。
  2. 比较次数从 $O(n^2)$ 降到 $O(n\log n)$,但移动次数不变。
  3. 总时间复杂度仍为 $O(n^2)$,因为移动是瓶颈。
  4. 稳定排序,原地排序。
  5. 只适用于顺序表(需要随机访问)。
  6. 408 考试重点:比较次数的变化和稳定性的判断。

记忆卡片

折半插入排序改进了什么?
只改进了比较次数($O(n^2)\to O(n\log n)$),移动次数不变。
总时间复杂度是多少?
仍是 $O(n^2)$,因为移动是瓶颈。
稳定吗?
稳定。
适用于链表吗?
不适用,需要随机访问做折半查找。
与直接插入排序的核心区别?
查找插入位置的方式:逐个比较 → 折半查找。
相等元素时如何继续?
继续向右(high 收缩),最终插入到 high+1,保证稳定。

交互动画 · 折半查找插入位置

已排序前缀 A[0..3] = [38, 49, 65, 97],插入元素 76 76 待插入 L H M 380 491 652 973 4 浅蓝 L/H/M = 折半查找的 low/high/mid;橙格 = 当前 mid;插入后 97 后移一位
点击「演示插入 76」查看折半查找插入位置的过程
比较 3 次,移动 1 次(97 后移)

相关知识点

direct-insertion-sort shell-sort binary-search