外部排序
1.多路归并排序
这种方法是根据内存的大小将一个大的文件数据分成多个长度为L的文件(又叫做顺串),然后分别将每个顺串读入内存进行排序(注:可以选用任何一种排序方法)之后输出到外存中,将已经拍好序的进行多路归并排序。即从每顺串中取出第一个数比较大小,并找个最小的那个数,将这个数充顺串中删除并输出,以后按此循环即可。
胜者树
在上面的多路归并的方法中,如果有K个顺串,每次有K个候选值,要找出其中的最小值,普通的做法需要进行K-1次比较,而使用败者树,则只需要O(logK)次比较,其原理就像我们平常的分组比赛,一个参赛者在小组出线之后,只需要与其他小组出线的参赛者比赛即可决出最后的冠军(最值),而不需要和其他所有参赛者都比一遍。
2.置换选择排序
选择置换算法用于生成顺串,在有限的内存限制下,它可以生成大概两倍于内存大小的顺串,其算法步骤如下:
假设内存中只有一个能容纳N个整型的数组:
- 首先从输入文件中读取N个数字将数组填满
- 使用数组中现有数据构建一个最小堆
重复以下步骤直到堆的大小变为0:
a.把根结点的数字A(即当前数组中的最小值)输出
b. 从输入文件中再读出一个数字B,若R比刚输出的数字A 大,则将B放到堆的根节点处,若B不比A大,则将堆的最后一个元素移到根结点,将B放到堆的最后一个位置,并把堆的大小缩减1(即新读入的数据没有进入堆中)
c. 在根结点处调用Siftdown重新维护堆
换一个输出文件,重新回到步骤(2)
解释:在以上算法运行过程中,步骤(3)每从最小堆中输出一个最小值,就从输入文件中再读入一个数据,若新读入的数比刚输出的数大,则可以属于当前的顺串,将其放入堆中即可,否则只能属于下一个顺串,需将其放在堆外,在运行过程中,堆的大小逐渐缩减直到0,此时就输出了一个顺串,而数组中新的数则可以用于构造一个新的堆,如此循环即可将原先的一个大文件转化成一个大概2N的顺串。至于为什么是2N,有一个比较抽象的类比证明:
假设在一条环形跑道上有一辆铲雪车在铲雪,且空中还在均匀地下着雪,那么当铲雪车已经沿着跑道开过一圈后,只要车速和降雪速度恒定,则跑道上的积雪量S也恒定,且车后积雪量最少,车前积雪量最多,如下图a。在这种情况下,设铲雪车每开一圈的时间,降雪量为X,车铲雪量为Y,则X,Y满足S+X - Y =S,即X = Y,又因为在铲雪车开一圈的过程中,铲掉的雪为原有的积雪加上降雪的一半,所以Y = S + X/2, 所以Y = 2S,即铲雪车铲掉了2S的雪量。而在选择置换中,数组的大小就相当于S,铲雪量就相当于输出顺串的大小,即2倍数组大。这个证明虽然有点抽象,但实际中只要输入文件中的数字是随机分布的,得到的顺串大小的确大概是所用数组大小的两倍。