您的位置:首页 > 其它

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

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

例如:

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)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: