排序算法及其效率分析(二)外排序

一般我们常见的冒泡,插入,选择,堆,快速或者归并都是要求数据在内存,即内部排序;但是当文件很大,不能全部放入内存时候,就需要进行外排序了:

例如:

int main(){
fstream inout;
inout.open("D:/large.dat",ios::out|ios::binary);
for(int i=0;i<2000000;i++)
{int val=rand();
inout.write(reinterpret_cast<char*>(&val),sizeof(val));
}
inout.close();
inout.open("D:/large.dat",ios::in|ios::binary);
int x;
for(int i=0;i<10;i++)
{inout.read(reinterpret_cast<char*>(&x),sizeof(x));
cout<<x<<" ";}
inout.close();
system("pause");
return 0;}
200万个int型数据存在"large.dat"的文件,需要进行外排序。

阶段1:反复从文件读取数据存入一个数组,使用内排序算法对数组排序,然后将数组写回一个临时文件。比如设置该数组规模MAX=100000,则有:

排序算法及其效率分析(二)外排序_第1张图片

阶段2:将每对有序数据段合并成一个大的有序数据段

实现阶段1代码:

int ini(int segment,char*oldfile,char*f1){
		int num=0;
	fstream in;
	in.open(oldfile,ios::in |ios::binary);
	fstream out;
	out.open(f1,ios::out|ios::binary);
	int *list=new int[segment];
	while(!in.eof()){
		int i;
		for(i=0;i<segment&&!in.eof();i++)
		in.read(reinterpret_cast<char*>(&list[i]),sizeof(list[i]));//从源文件读取到数组里
	if(in.eof())i--;
	if(i<=0) break;
	else num++;
	quicksort(list,i);//使用内部排序函数,比如插入,归并等排序
	for(int j=0;j<i;i++)
	out.write(reinterpret_cast<char*>(&list[j]),sizeof(list[j]));
	}//将排序好数组存在临时文件f1里
	delete []list;
	in.close();
	out.close();}
实现阶段2

排序算法及其效率分析(二)外排序_第2张图片

首先将f1文件的前一半数据复制到f2,然后将f1与f2数据段对应合并,写入临时文件f3

	//将f1中Num个数据段分半,前一半到f2
	void copyfile(int num,int segmentsize,fstream &f1,fstream&f2){
	int i;
	for(i=0;i<(num/2)*segmentsize;i++)
	  {int x;
	f1.read(reinterpret_cast<char*>(&x),sizeof(x));
	f2.write(reinterpret_cast<char*>(&x),sizeof(x));}
	}
下面是真正合并阶段:

由于每次合并都是两个数据段的合并,所以首先实现两个数据段的合并:

//合并两个数据段
	void mergetwoseg(int segmentsize,fstream&f1,fstream&f2,fstream&f3){
	int fromf1;
	f1.read(reinterpret_cast<char*>(&fromf1),sizeof(fromf1));
	int fromf2;
	f2.read(reinterpret_cast<char*>(&fromf2),sizeof(fromf2));
	int f1count=1;
	int f2count=1;
	while(1){
	if(fromf1<fromf2)
		{f3.write(reinterpret_cast<char*>(&fromf1),sizeof(fromf1));
	if(f1.eof()||f1count++>=segmentsize){if(f1.eof())break;f3.write(reinterpret_cast<char*>(&fromf2),sizeof(fromf2));break;}
	else {f1.read(reinterpret_cast<char*>(&fromf1),sizeof(fromf1));}
	}
	else{
		f3.write(reinterpret_cast<char*>(&fromf2),sizeof(fromf2));
		if(f2.eof()||f2count>=segmentsize){if(f2.eof())break;f3.write(reinterpret_cast<char*>(&fromf1),sizeof(fromf1));break;}
		else {f2.read(reinterpret_cast<char*>(&fromf2),sizeof(fromf2));}}
	}
	while(!f1.eof()&&f1count++<segmentsize){
	int x;
	f1.read(reinterpret_cast<char*>(&x),sizeof(x));
	if(f1.eof())break;
	f3.write(reinterpret_cast<char*>(&x),sizeof(x));}
	while(!f2.eof()&&f2count++<segmentsize){
	int y;
	f2.read(reinterpret_cast<char*>(&y),sizeof(y));
	if(f2.eof())break;
	f3.write(reinterpret_cast<char*>(&y),sizeof(y));}
	}
最后是合并所有的:

void merigefile(int num,int segmentsize,char*f1,char*f2,char*f3){
	for(int i=0;i<num;i++)
   mergetwoseg(segmentsize,f1,f2,f3);
	while(!f1.eof()){
	int x;
	f1.read(reinterpret_cast<char*>(&x),sizeof(x));
	if(f1.eof())break;
	f3.write(reinterpret_cast<char*>(&x),sizeof(x));}}

算法性能:在阶段1从源文件读取n个元素并写入临时文件,开销O(n);在阶段2,有序数组数目n/num,每个合并步骤都将数目减少一半,log(n/num)步骤后,数目为1,每次合并从f1读取一半数据段写入f2,f1剩余数据段与f2中数据段进行合并,每次合并O(n),故O(n)*log(n/num)=O(nlogn)


你可能感兴趣的:(排序算法及其效率分析(二)外排序)