首页/数据结构/07-sorting/外部排序 🔗 在 Obsidian 中打开
数据结构 · 07-sorting

外部排序

难度 ★★★★重要度 ★★ 考查频率 低题型 综合应用 数据结构/排序外部排序归并排序多路归并
速查
数据量超出内存时,外部排序 = 生成初始归并段 + 多路归并。归并趟数 $\lceil\log_k m\rceil$;用置换选择生成长段、用最佳归并树最小化读写、用败者树把比较降到 $O(\log k)$。

速查

项目
主题外部排序
核心概念数据太大无法全装入内存时,用归并排序(对磁盘顺序访问最友好)
时间复杂度$O(k)$、$O(\log_k m)$(趟数)
难度⭐⭐⭐⭐

核心概念

当数据量太大,无法全部装入内存时,需要外部排序。外部排序主要使用归并排序,因为归并排序对磁盘的顺序访问特性最友好。

基本过程

1. 生成初始归并段(run):
   - 每次将能装入内存的数据排序后写回磁盘
   - 生成若干个有序的归并段

2. 多路归并:
   - 将 k 个归并段进行 k 路归并
   - 每次从 k 个段中取最小的元素输出
   - 直到所有段归并为一个

关键问题

归并趟数

设初始归并段数为 $m$,归并路数为 $k$:

$$ \text{归并趟数} = \lceil \log_k m \rceil $$

减少趟数的方法

  • 增大 $k$(多路归并)
  • 减少 $m$(增大初始归并段长度)

置换选择排序

用于生成更长的初始归并段

1. 从磁盘读入尽可能多的记录到内存
2. 选出最小值输出(属于当前归并段)
3. 从磁盘读入下一个记录
4. 若新记录 ≥ 刚输出的记录,属于当前段;否则属于下一段
5. 重复,直到当前段结束,开始新段

平均可生成长度为内存大小 2 倍的归并段。

最佳归并树

类似哈夫曼树,构造带权路径长度最小的归并策略:

  • 将归并段长度作为权值
  • 构造哈夫曼树
  • 每次归并长度最小的两个段
补虚段k 路归并时,若初始段数不满足 $(m-1) \bmod (k-1) = 0$,需要补虚段(长度为 0 的段)。

k 路归并的败者树

k 路归并时,每次选出最小值需要比较 $k-1$ 次。使用败者树可以减少到 $\lceil \log_2 k \rceil$ 次。

败者树:完全二叉树
- 叶节点:k 个归并段的当前最小值
- 内节点:记录"败者"(较大值)
- 根节点:记录"胜者"(最小值)

选最小值后,只需沿着胜者路径更新,比较次数 = 树的高度

磁盘读写次数分析

$$ \text{总读写次数} = 2 \times m \times (1 + \lceil \log_k m \rceil) $$
  • 每趟归并:读一次所有数据 + 写一次所有数据
  • 初始归并段生成:读一次 + 写一次

常见考法

考点说明
归并趟数计算给定段数和路数,求趟数
置换选择排序手算生成初始归并段
最佳归并树构造哈夫曼树,求读写次数
败者树理解败者树的工作原理
补虚段k 路归并时如何补虚段

易错点

注意
  1. 外部排序用归并排序,不是快排或堆排序。
  2. 归并趟数 = $\lceil\log_k m\rceil$,不是 $\log_2$。
  3. 最佳归并树是哈夫曼树的变形。
  4. 败者树中,根节点存的是胜者(最小值),不是败者。
  5. 补虚段条件:$(m-1) \bmod (k-1) \neq 0$ 时需要补。

核心结论

  1. 外部排序 = 生成初始归并段 + 多路归并。
  2. 减少趟数:增大归并路数 $k$ 或减少初始段数 $m$。
  3. 置换选择排序可生成约 2 倍内存大小的归并段。
  4. 最佳归并树使总读写次数最小。
  5. 败者树使 k 路归并的比较次数从 $O(k)$ 降到 $O(\log k)$。

记忆卡片

外部排序为什么用归并排序?
归并排序对磁盘的顺序访问特性最友好,适合外存数据排序。
置换选择排序的作用?
生成约 2 倍内存大小的初始归并段,减少归并趟数。
败者树的作用?
使 k 路归并的比较次数从 $O(k)$ 降到 $O(\log k)$,减少内部归并时间。
最佳归并树的作用?
使总读写次数最小,本质是哈夫曼树的应用。
减少趟数的方法?
增大归并路数 $k$,或减少初始段数 $m$(用置换选择排序)。
什么时候需要补虚段?
k 路归并时 $(m-1) \bmod (k-1) \neq 0$ 需补长度为 0 的虚段。

交互动画 · 3 路归并

3 个初始归并段(每段有序) 段0 段1 段2 3 7 15 5 9 20 1 11 25 归并结果 灰格 = 已输出;橙格 = 当前三个段的首元素(取最小输出);右侧逐步生成有序结果
点击「演示 3 路归并」逐步取出三个段的最小值
每步取三段当前首元素的最小值

相关知识点

internal-sorting-comparison