
(以下内容全部出自上述课程)
目录
- 外部排序
-
- [1. 外存、内存之间的数据结构](#1. 外存、内存之间的数据结构)
- [2. 外部排序原理](#2. 外部排序原理)
-
- [2.1 构造初始归并段](#2.1 构造初始归并段)
- [2.2 第一趟归并](#2.2 第一趟归并)
- [2.3 第二趟归并](#2.3 第二趟归并)
- [2.4 第三趟归并](#2.4 第三趟归并)
- [3. 时间开销分析](#3. 时间开销分析)
- [4. 优化](#4. 优化)
-
- [4.1 多路归并](#4.1 多路归并)
- [4.2 减少初始归并段数量](#4.2 减少初始归并段数量)
- [5. 小结](#5. 小结)
- 败者树
-
- [1. 多路平衡归并带来的问题](#1. 多路平衡归并带来的问题)
- [2. 败者树的构造](#2. 败者树的构造)
- [3. 败者树的使用](#3. 败者树的使用)
- [4. 败者树的实现思路(了解)](#4. 败者树的实现思路(了解))
- [5. 小结](#5. 小结)
- 置换-选择排序
-
- [1. 土办法构造初始归并段](#1. 土办法构造初始归并段)
- [2. 置换-选择排序](#2. 置换-选择排序)
- [3. 小结](#3. 小结)
- 最佳归并树
-
- [1. 归并树的神秘性质](#1. 归并树的神秘性质)
- [2. 构造最佳归并树](#2. 构造最佳归并树)
-
- [2.1 2路](#2.1 2路)
- [2.2 多路归并](#2.2 多路归并)
- [3. 添加虚段的数量](#3. 添加虚段的数量)
- [4. 小结](#4. 小结)
外部排序

1. 外存、内存之间的数据结构
特点 :外存大,存储数据多;内存小,存储数据少
规定 :外存的数据只有读到内存之后才能进行修改,修改之后还要写回到外存(可以写回原来不同的位置)

2. 外部排序原理
简单来说,因为外存数据又多又杂,就要一点一点读入到内存中排序,然后再写回外存。
逻辑 :外存-->读出数据-->内存-->在内存有序排列数据(比如一个块三个数据632排成236)-->写回外存

2.1 构造初始归并段
因为内存腾出了两个存放数据块的位置(输入缓冲区)和写出数据的位置(输出缓冲区),2-->1,二合一就是二路归并
归并排序具体可见:归并排序
从外存读入两个数据块放入内存的输入缓冲区内,然后就可以对这两个数据块进行归并排序了。

得到两个有序数列

将这两个有序数列写回外存,这样就构成了一个有序的归并段

经过16次读和16次写,外存就变成了左边的8个归并段

2.2 第一趟归并
接下来就需要我们把归并段们继续归并排序:
- 将两个归并段中小的数据块读入内存
- 在内存中进行归并排序
- 将排序好的数据写回外存
- 注意 :写回的位置是另外一片,不是原来的存放未排序数据的位置


出现一整个缓冲区空白的情况,就继续读入一个数据块,然后继续进行归并排序:




第一趟归并结束后,就成功得到了四个大的归并段:

2.3 第二趟归并
和之前一样,挑出两个归并段中最小的一组,继续进行合并:



第二趟合并后,就得到了两个更大的归并段:

2.4 第三趟归并
最后,把这两个更大的归并段进行合并:

最终就得到了有序的数据:

3. 时间开销分析
- 读写外存的时间:就是构造初始归并段的时间
- 内部排序所需时间:就是排序花费的时间。
- 内部归并所需时间 :就是几次归并花费的时间。

4. 优化
我们先对读写外存的这部分时间进行优化。

4.1 多路归并
我们可以在内存多申请几个输入缓冲区,实现多路归并,一次读入两个数据段和一次读入四个数据段,当然是后者I/O次数更小,还可以减少归并的趟数。
假设:第一趟归并用4路归并


第一次归并后就得到两个归并段,只需要再一趟就可以完全有序:

4.2 减少初始归并段数量
比如之前的我们的初始归并段是8个,现在减少为4个:

4-->2-->1,得到初始归并段后再归并两次就可以完全有序了

外存的总数据块数不变,初始归并段越长,初始归并段越少。

5. 小结

重点在平衡 这两个字上:

败者树
1. 多路平衡归并带来的问题
多路平衡归并 :一个数要和很多个数都要比一次,才能找到最小的那个数字,就很浪费时间

2. 败者树的构造
就是两个人战斗,失败的留下,胜利的上去,继续和另一组胜利的人继续比,最终选出一个冠军。

这个时候冠军跑路了:

由派大星顶替它原来的位置

那么难道我们还要重新比7次么?!

答案是不需要,右侧的根本没被派大星影响,右侧胜利的依旧是孙悟空;
而派大星需要代替天津饭,先和阿乐比,赢了再和程龙比,赢了再和孙悟空比;
只需要比对三次就可以了

3. 败者树的使用
同样的规则,我们就可以搬到归并段上使用。
画出败者树:

每个归并段都排除自己最小的一个数字参赛:
- 27>12,27留,12上 ;1<17,17留,1上 ;2<9,9留,2上 ;11>4,11留,4上;
- 12>1,12留,1上 ;2<4,4留,2上
- 1<2,2留,1上;
- 这样就找出了最小的一个数

注意 :结点中记录的是来自的归并段 ,而不是真实数字

所以再需要接着比赛选出最小的冠军,就只需要踩着前人的脚印,继续向上比拼。
比如: - 上一次的冠军是1,来自归并段3,他就可以先走了
- 继续用归并段3内的数字接力,6出来沿着1的比赛路径进行比拼
- 6<17,17留,6上,所以结点是6所在的归并段3;
- 12>6,12留,6上,所以结点是6所在的归并段3;
- 最终回合:6>2,6留,2上,所以结点是2所在的归并段5;

4. 败者树的实现思路(了解)


5. 小结


置换-选择排序

1. 土办法构造初始归并段
纯一块一块构造。


2. 置换-选择排序
- 工作区:只能放三个记录
- MINIMAX:刚刚写出的数据的值
- 规定:同一归并段,只能写出比MINIMAX大的数字
- 注意 :这里的输出缓冲区省略了,但不代表没有

选择工作区内最小的但比4大的数字写出:

按照这个规律一直写出:
这里我们发现最小的一个数字是10<13,而我们只能写出比13大的,所以就放着先不要动

当工作区内的数字都比MINIMAX小,不可以写出的时候:

我们就需要开一个新的归并段,经过写出最后是这样:

最后就得到了三个不定长的归并段:

注意:是有输出缓冲区存在的,只不过我们省略了而已

3. 小结

最佳归并树
1. 归并树的神秘性质
归并过程中的磁盘读写次数=归并树的WPL*2
哈夫曼树具体可见:哈夫曼树
也就是最佳归并树是哈夫曼的形式

2. 构造最佳归并树
2.1 2路

2.2 多路归并


如果减少一个归并段,那么单纯地按照哈夫曼树的逻辑处理得到的就不是最佳归并树了:

我们需要添加一个结点0,然后再按照正常的步骤继续进行:


3. 添加虚段的数量
情景 :类似于上面缺少一个归并段的时候,我们需要添加几个结点0?
结论 :图片最后两行(代入例子更好理解)

4. 小结

