首页/数据结构/07-sorting/置换选择排序和最佳归并树 🔗 在 Obsidian 中打开
数据结构 · 07-sorting

置换选择排序和最佳归并树

重要度 ★★ 数据结构/排序排序外部排序置换选择排序最佳归并树
速查
外部排序总时间主要花在磁盘 I/O 上:用置换选择排序减少归并段数(平均段长由 $m$ 增至约 $2m$),用最佳归并树(哈夫曼树)安排归并顺序使总 I/O 最少,归并趟数 $= \lceil \log_k m\rceil$。

外部排序的优化问题

外部排序的总时间主要取决于:

  1. 磁盘 I/O 次数(最主要)
  2. 内部排序和归并的时间

因此优化方向有两个:

  • 减少归并段数 → 用置换选择排序
  • 减少归并趟数 → 用最佳归并树

一、置换选择排序(Replacement Selection Sort)

目的

生成更长的初始归并段,从而减少归并段数量。普通方法:内存能装 $m$ 个元素,每个归并段长度为 $m$;置换选择排序:归并段平均长度为 $2m$

基本思想

  1. 从外存读入 $m$ 个元素到内存(用败者树 / 最小堆维护)
  2. 输出当前最小值到归并段
  3. 从外存读入下一个元素:
    • 若新元素 $\geq$ 刚输出的值 → 加入当前归并段
    • 若新元素 $<$ 刚输出的值 → 冻结,留到下一个归并段
  4. 当所有 $m$ 个元素都被冻结时,当前归并段结束
  5. 解冻所有元素,开始新的归并段

手算示例

内存大小为 4,输入序列:12, 25, 38, 10, 45, 5, 15, 30, 50, 3, 20, 60

步骤输出内存(最小堆)读入判断
110[12, 25, 38]45$45 \geq 10$,加入
212[25, 38, 45]5$5 < 12$,冻结
325[38, 45, (5)]15$15 < 25$,冻结
438[45, (5), (15)]30$30 < 38$,冻结
545[(5), (15), (30)]50$50 \geq 45$,加入
650[(5), (15), (30)]3$3 < 50$,冻结

此时所有元素都被冻结,第 1 段结束:10, 12, 25, 38, 45, 50(长度 6,比内存大小 4 更长!)。第二段解冻 [5,15,30,3] 继续,得到 3, 5, 15, 20, 30, 60。共 2 个归并段(普通方法需 $12/4=3$ 个)。

二、最佳归并树

目的

当各归并段长度不同时,如何安排归并顺序使总 I/O 次数最少

基本思想

最佳归并树是一棵哈夫曼树,以各归并段的长度为权值构造。总 I/O 次数为:

$$ \text{I/O} = 2 \times \sum (\text{叶子权值} \times \text{路径长度}) $$

(乘 2 是因为每次归并都要读一次、写一次。)

手算示例

5 个归并段,长度分别为 2, 3, 5, 7, 11:

  • 选最小两个 2, 3 → 合并为 5
  • 选最小两个 5, 5 → 合并为 10
  • 选最小两个 7, 10 → 合并为 17
  • 合并 11, 17 → 28

$$ \text{I/O} = 2(2\!\times\!4 + 3\!\times\!4 + 5\!\times\!3 + 7\!\times\!2 + 11\!\times\!1) = 2\times 60 = 120 $$

k 叉最佳归并树若初始归并段数 $m$ 不满足 $(m-1)\bmod(k-1)=0$,需补长度为 0 的虚段

三、多路归并与归并趟数

设初始归并段数为 $m$,做 $k$ 路归并,则归并趟数

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

减少归并趟数的方法:

  • 增加 $k$(更多路归并)→ 但 $k$ 增大会增加每趟比较次数
  • 减少 $m$(更少初始归并段)→ 用置换选择排序

关键性质

主题结论
置换选择排序平均段长$\approx 2m$(输入随机时)
归并段数$\approx n/(2m)$,比普通方法 $n/m$ 减少一半
最佳归并树本质哈夫曼树,权值 = 归并段长度
补虚段条件$(m-1)\bmod(k-1)\neq 0$ 时需补

易错点

注意
  1. 置换选择排序的归并段长度不固定,取决于输入分布。
  2. 最佳归并树是哈夫曼树,不是任意二叉树。
  3. I/O 次数要乘 2(读一次 + 写一次)。
  4. 补虚段公式:$(m-1)\bmod(k-1)\neq 0$ 时需要补。

核心结论

优化方法作用效果
置换选择排序减少归并段数平均段长从 $m$ 增至 $2m$
最佳归并树减少 I/O 次数按哈夫曼树归并,I/O 最少
增加路数 $k$减少归并趟数趟数 $= \lceil\log_k m\rceil$

记忆卡片

置换选择相比普通方法优势?
归并段平均长度从 $m$ 增至约 $2m$,段数减半。
最佳归并树本质是什么?
哈夫曼树,使总 I/O 次数最少。
何时补虚段?
$(m-1)\bmod(k-1)\neq 0$ 时补长度为 0 的段。
何时把新元素加入当前段?
新元素 $\geq$ 刚输出的最小值;否则冻结。

交互动画 · 最佳归并树构建

28 11 17 7 10 5 5 2 3 5
点击「下一步」逐步合并最小的两个归并段
内部节点权值和 = 0 ,I/O 次数 = 0

相关知识点

(暂无关联知识点)