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

来源:互联网 发布:阿里云 宕机 经济损失 编辑:程序博客网 时间:2024/06/06 13:11

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

例如:

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,则有:


阶段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

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

//将f1中Num个数据段分半,前一半到f2void 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)


0 0
原创粉丝点击