一般我们常见的冒泡,插入,选择,堆,快速或者归并都是要求数据在内存,即内部排序;但是当文件很大,不能全部放入内存时候,就需要进行外排序了:
例如:
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个数据段分半,前一半到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)