外部文件多路平衡归并类似于内部排序的归并算法。可知增加分段个数k可以减少归并次数s,从而减少外存读写次数,但是单纯增加k将会导致增加内部归并的时间。
对于k-路归并,令u个记录分布在k个归并段上,显然,归并后的第一个记录应该是k个归并段中关键字最小的记录,即应从每个归并段的第一个记录的相互比较中选出最小者,这需要进行k-1次比较。同理,每得到归并后的有序段中的一个记录,就要进行k-1次比较。显然,为得到含u个记录的归并段需要进行(u-1)(k-1)次比较。由此,对n个记录的文件进行外部排序时,在内部归并过程中进行的总的比较次数为s(k-1)*(n-1)。由于随着k的增长,内部归并时间亦会增长,这将抵消掉由于增大k而减少外存信息读写时间所得的收益,这是我们所不希望的。然而,若在k-路归并时利用“败者树”,则可以使在k个记录中选出关键字最小的记录时仅需进行log2k次比较,从而使总的归并时间变小。
对于败者树的定义,请参考相关数据结构书籍,这里不作冗余叙述。
由于各路数据要防止归并过程中提前取空的问题,代码设计会有些变动,详见以下代码:
void k_merge()
{
unsigned long int no;
int p;
double tempd;
for(int i=0;i<nfrag;i++)
{
p=poss[i];
fsortin.seekg((poss[i])*sizeof(double),std::ios::beg);
fsortin.read((char*)&tempd,sizeof(double));
external[i]=tempd;
poss[i]++;
}
create_losertree();
no=0;
while (no<maxnum)
{
p=losertree[0];
fsortout.write((char*)&(external[p]),sizeof(double));
no++;
if(poss[p]-p*maxnumin>=nums[p]){external[p]=MAXKEY;}
else
{
fsortin.seekg((poss[p])*sizeof(double),std::ios::beg);
fsortin.read((char*)&tempd,sizeof(double));
external[p]=tempd;
poss[p]++;
}
adjust(p);
}
}
void create_losertree()
{
external[nfrag]=MINKEY;
for(int i=0;i<nfrag;i++) losertree[i]=nfrag;
for(int i=nfrag-1;i>=0;i--) adjust(i);
}
void adjust(int s)
{
int t=(s+nfrag)/2;
int temp;
while (t>0)
{
if(external[s]>external[losertree[t]])
{
temp=s;
s=losertree[t];
losertree[t]=temp;
}
t=t/2;
}
losertree[0]=s;
}