多路归并排序
多路归并排序算法在常见数据结构书中都有涉及。从2路到多路(k路),增大k可以减少外存信息读写时间,但k个归并段中选取最小的记录需要比较k-1次,为得到u个记录的一个有序段共需要(u-1)(k-1)次,若归并趟数为s次,那么对n个记录的文件进行外排时,内部归并过程中进行的总的比较次数为s(n-1)(k-1),若共有m个归并段,则s=logkm,所以总的比较次数为: (向上取整)(logkm)(k-1)(n-1)=(向上取整)(log2m/log2k)(k-1)(n-1),而(k-1)/log2k随k增而增因此内部归并时间随k增长而增长了,抵消了外存读写减少的时间,这样做不行,由此引出了“败者树”tree of loser的使用。在内部归并过程中利用败者树将k个归并段中选取最小记录比较的次数降为(向上取整)(log2k)次使总比较次数为(向上取整)(log2m)(n-1),与k无关。
败者树
叶子节点记录k个段中的最小数据,然后两两进行比赛。败者树是在双亲节点中记录下刚刚进行完的这场比赛的败者,让胜者去参加更高一层的比赛。决赛,根节点记录输者,所以需要重建一个新的根节点,记录胜者(如下图节点0)。
示例:我们这里以四路归并为例,假设每个归并段已经在输入缓冲区如下图。
每路的第一个元素为胜利树的叶子节点,(5,7)比较出5胜出7失败成为其根节点,(29,9)比较9胜出29失败成为其根节点,胜者(5,9)进行下次的比赛9失败成为其根节点5胜出输出到输出缓冲区。由第一路归并段输出,所有将第一路归并段的第二个元素加到叶子节点如下图:
加入叶子节点16进行第二次的比较,跟胜利树一样,由于右子树叶子节点没有发生变化其右子树不用再继续比较。
K路归并排序的实现
#include <iostream>
using namespace std;
#define LEN 10 //最大归并段长
#define MINKEY -1 //默认全为正数
#define MAXKEY 100 //最大值,当一个段全部输出后的赋值
struct Array
{
int arr[LEN];
int num;
int pos;
}*A;
int k,count;
int *LoserTree,*External;
void Adjust(int s)
{
int t=(s+k)/2;//ls[t]是b[s]的双亲结点的下标
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;
}
void CreateLoserTree()
{
External[k]=MINKEY;
int i;
for(i=0;i<k;i++)LoserTree[i]=k;//设置ls中败者的初值,简化了败者树的建立过程,这样就可以直接通过(调整过程)来建立败者树。
for(i=k-1;i>=0;i--)Adjust(i);
}
void K_Merge()
{
int i,p;
//初始化External数组,用以接下来创建LoserTree
for(i=0;i<k;i++)
{
p = A[i].pos;
External[i]=A[i].arr[p];
//cout<<External[i]<<",";
A[i].pos++;
}
//创建LoserTree
CreateLoserTree();
int NO = 0;
//输出最小值,并替代调整
while(NO<count)
{
p=LoserTree[0];
cout<<External[p]<<",";
NO++;
if(A[p].pos>=A[p].num)External[p]=MAXKEY;
else
{
External[p]=A[p].arr[A[p].pos];
A[p].pos++;
}
Adjust(p);
}
cout<<endl;
}
int main()
{
//freopen("in.txt","r",stdin);
int i,j;
count=0;
cin>>k;
A=(Array *)malloc(sizeof(Array)*k);
for(i=0;i<k;i++)
{
cin>>A[i].num;
count=count+A[i].num;
for(j=0;j<A[i].num;j++)
{
cin>>A[i].arr[j];
}
A[i].pos=0;
}
LoserTree=(int *)malloc(sizeof(int)*k);
External=(int *)malloc(sizeof(int)*(k+1));
K_Merge();
system("pause");
return 0;
}