败者树 K-路归并排序

         外部排序指的是大文件的排序,即待排序的记录存储在外存储器上,待排序的文件无法一次装入内存,需要在内存和外部存储器之间进行多次数据交换,以达到排序整个文件的目的。外部排序最常用的算法是多路归并排序,即将原文件分解成多个能够一次性装入内存的部分,分别把每一部分调入内存完成排序。然后,对已经排序的子文件进行多路归并排序。

    多路归并排序算法在常见数据结构书中都有涉及。从2路到多路(k路),增大k可以减少外存信息读写时间,但k个归并段中选取最小的记录需要比较k-1次,为得到u个记录的一个有序段共需要(u-1)(k-1)次,若归并趟数为s次,那么对n个记录的文件进行外排时,内部归并过程中进行的总的比较次数为s(n-1)(k-1),也即(向上取整)(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个叶子结点、k-1个比较结点、1个冠军结点共2k个。ls[0]为冠军结点,ls[1]--ls[k-1]为比较结点,ls[k]--ls[2k-1]为叶子结点(同时用另外一个指针索引b[0]--b[k-1]指向)。另外bk为一个附加的辅助空间,不属于败者树,初始化时存着MINKEY的值。

    多路归并排序算法的过程大致为:首先将k个归并段中的首元素关键字依次存入b[0]--b[k-1]的叶子结点空间里,然后调用CreateLoserTree创建败者树,创建完毕之后最小的关键字下标(即所在归并段的序号)便被存入ls[0]中。然后不断循环:把ls[0]所存最小关键字来自于哪个归并段的序号得到为q,将该归并段的首元素输出到有序归并段里,然后把下一个元素关键字放入上一个元素本来所在的叶子结点b[q]中,调用Adjust顺着b[q]这个叶子结点往上调整败者树直到新的最小的关键字被选出来,其下标同样存在ls[0]中。循环这个操作过程直至所有元素被写到有序归并段里。

以上资料借鉴 http://chenkegarfield.blog.163.com/blog/static/62330008200910249526638/

参考数据结构(C语言版)严蔚敏教材P298页伪代码,用C++实现

代码如下:

#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;
	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;
	for(i=k-1;i>=0;i--)Adjust(i);
}

void K_Merge()
{
	int i,p;
	for(i=0;i<k;i++)
	{
		p = A[i].pos;
		External[i]=A[i].arr[p];
		//cout<<External[i]<<",";
		A[i].pos++;
	}
	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();

	return 0;
}

输入文件 如下:

5
5
1 5 6 8 25
6
2 6 9 25 30 32
3
5 9 16
6
6 9 15 24 30 36
2
8 34

  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值