目录
一、外部排序概念
对大文件进行排序。而文件中记录很多,无法将整个文件复制到内存中进行排序,因此,需要将待排序的记录存储在外存上,排序时再把数据一部分一部分地调入内存进行排序,在排序过程中需要进行多次内存和外存之间的交换。
文件通常是按块存储在磁盘上,操作系统也是按块对磁盘上的信息进行I/O。因为磁盘I/O的动作所需时间远大于内存中运算的时间,因此外部排序中的时间代价主要为I/O次数。
二、归并排序
步骤:1、根据内存缓冲区的大小,将外存上的文件分成若干长度为x的子文件,依次读入内存并利用内部排序算法进行排序,并将排序后的有序子文件(归并段)重新写回外存。
2、对这些归并段进行逐趟归并,使归并段逐渐由小到大,直至得到整个有序文件。
若有N个记录,内存工作区可以容纳L个记录,则初始归并段数量r=N/L
以二路归并,2000个记录文件,每个磁盘块都含125条记录为例,共16个磁盘块:
1.初始归并段数量r=8。每个段含有250条记录。首先通过8次内部排序得到8个初始归并段R1~R8。
2.将8个归并段进行两两归并,直到得到一个有序文件
假设把内存工作区等分为2个输入缓冲区,1个输出缓冲区。把R1,R2分别读入一个块,分别放在2个输入缓冲区中,在内存中对R1,R2进行归并排序,归并后的对象放在输出缓冲区中,若输出缓冲区满了,就将其顺序写到输出归并(R1`)段中,然后清空输出缓冲区,继续存放后续内容。若某个输入缓冲区取空了,则读取下一块,继续归并。
外部排序总时间=内部排序的时间+外存信息读/写的时间+内部归并的时间
如图所示,进行了三趟归并。内部排序读写次数=16+16。每趟需要读16次写16次。总共时间为32*3+32(内部排序的次数) 次读写。若增大归并路数,可减少归并趟数,进而减少总IO次数。
对r个初始归并段(先经过内部排序得到),做k路平衡归并。第一趟可以将r个初始归并段归并为个归并段,依次类推,直至形成一个大的归并段为止。
归并趟数S=树的高度-1=
因此,增大归并路数k,或者减少初始归并段个数r(增大初始归并段长度),都能减少S
三、多路平衡归并与败者树
目的:减少关键字比较次数,减少归并趟数
由归并排序可知,增加归并路数k能减少归并趟数s,进而减少io次数。但是增加归并路数k时,内部归并的时间也将增加。为使内部归并不受k的增大的影响,引入败者树,可视为完全二叉树
过程:k个叶节点分别存放k个归并段,内部结点记忆左右子树的失败者,让胜利者往上进行比较,一直到根节点。大者为失败者,小者为胜利者。根节点所指为最小数
k路归并的败者树深度为,则从k个记录中选择最小关键字,需
次比较。
总的比较次数约为
因此,使用败者树后,内部归并的比较次数与k无关。只要内存空间允许,增大归并路数k将有效减少归并树的高度,从而减少io次数,提高外部排序的速度
k不是越大越好,k增大时,相应地需要增加输入缓冲区的个数。若可供使用的内存空间不变,势必要减少每个输入缓冲区的容量,使得内存,外村交换数据的次数增大。当k过大时,虽然归并趟数会减少,但读写外存的次数仍会增加
四、置换-选择排序(生成初始归并段)
目的:减少初始归并段的数量
由归并排序可知,减少初始归并段个数r也能减少趟数,归并段的个数r= n为记录数,l为每个归并段的长度,而每个初始归并段的长度是相同的,所以需要产生更长的初始归并段
实例见p397
五、最佳归并树
文件经过置换-选择排序后,得到的是长度不等的初始归并段。如何组织长度不等的初始归并段的归并顺序,使得io次数最少?
设各叶节点表示一个初始归并段,叶节点到根节点的路径长度表示参加归并的趟数,各非叶节点代表归并成的新归并段,根节点表示最终的归并段。则io次数=2*WPL
优化归并树的WPL:让记录数少的初始归并段最先归并,记录数最多的最晚归并。m路归并排序->m叉树
若初始归并段数不是m的整数倍,即不足以构成一个严格的m叉树,则需要添加长度为0的虚段,权为0的叶子应里根最远。
如何判定添加虚段的数目:
设度为0的结点有n0个,度为k的结点有nk个,归并树的结点总数为n,则有
n=nk+n0 n=k*nk+1 ->nk=(n0-1)/(k-1)
若(n0-1)%(k-1)=0,说明这n0个叶节点正好可以构成k叉归并树,此时内节点有nk个。
若=u≠0,说明有u个多余,不能包含在k叉归并树中。需加上k-u-1个空归并段