海量数据排序

来源:互联网 发布:眼镜数据 编辑:程序博客网 时间:2024/05/08 09:39
  排序分为内部排序和外部排序两大类,若要排序的元素很多,整个序列的排序过程不可能在内存中完成,则此类排序就称为外部排序。针对外部排序这种问题,下面我们来介绍一种方法。
  由于要排序的数据量很大,不可能一次性加载到内存,所以我们就采取"分而治之"的方法。从大文件中读取一部分数据加载到内存进行快速排序,再将有序的序列再放回到小文件中。最后再使用归并排序的思想,对这些小文件进归并处理,使得最后只剩下一个文件。则最后的这个文件里面记录的就是排好序的数据。

算法流程:




举个栗子:

问:有40亿个整数,只有1G的内存,对这些整数进行排序。

分析:
  40个整数有160亿个字节,也就是16G的内存才能放下,所以直接加载到内存中排序是不现实的,我们可以将这40亿个整数放到20个小文件中,平均每个小文件是0.8G大约800M左右。我们分别将每个文件都加载到内存中对他们使用快速排序,然后再将排序好的文件写入到文件中。等到这20个小文件都全部有序的时候,我们再对这些小文件进行归并排序,最后将这20个小文件合并成一个文件,这样就将这40亿个整数排好序了。

代码实现:
#pragma once#include<vector>#include<cassert>#include<cstdio>#include<stdlib.h>#include<algorithm>using namespace std;class BigDataSort{public:BigDataSort(char * bigfile, int n, size_t limit)    //n表示要切分成多份少小文件:_bigfile(bigfile)                              //bigfile表示要排序的文件名, _limit(limit)                                 //_limit代表小文件中最多存放的数据个数{_SmallFileName.resize(n);for (int i = 0; i < n;++i)                      //先创建n个文件名{char name[128] = {0};_itoa(i,name,10);_SmallFileName[i] = name;}}void Sort(){SmallFileSort();            //小文件排序SmallFileMerge();           //小文件归并}protected:void SmallFileSort()           //将大文件分成多份,排好序之后再写入小文件{FILE *fRead = fopen(_bigfile, "rb");           //打开大文件assert(fRead);for (size_t i = 0; i < _SmallFileName.size(); ++i){vector<int> v;for (size_t count = 0; count < _limit; ++count)    //将这些小文件中的数据加载到内存{int num = 0;if (_GetNum(fRead, num))               //如果读取成功,则将它放到vector中v.push_back(num);elsebreak;}//对这些数据进行快速排序sort(v.begin(),v.end());//将这些数据再写到文件中FILE *fWrite = fopen(_SmallFileName[i].c_str(), "wb");assert(fWrite);for (size_t i = 0; i <v.size(); ++i)       //将这些小文件中的数据加载到内存{_InputNum(fWrite,v[i]);         //向文件中写入一个数}fclose(fWrite);}fclose(fRead);}//对这些文件进行归并排序void SmallFileMerge(){while (_SmallFileName.size()>1)       //将这些小文件最后合并的只剩一个文件{vector<string> v;while (_SmallFileName.size() > 1){string filename1 = _SmallFileName.back();_SmallFileName.pop_back();string filename2 = _SmallFileName.back();_SmallFileName.pop_back();FILE *fRead1 = fopen(filename1.c_str(), "rb");assert(fRead1);FILE *fRead2 = fopen(filename2.c_str(), "rb");assert(fRead2);string newfile = filename1 + filename2;     //创建一个新的文件名,用来存放这两个文件合并后的结果v.push_back(newfile);                       //将新文件保存起来FILE* fWrite = fopen(newfile.c_str(), "wb");assert(fWrite);_Merge(fRead1,fRead2,fWrite);               //对这两个文件中的数据进行归并排序fclose(fRead1);fclose(fRead2);fclose(fWrite);remove(filename1.c_str());                   //删除这些已经合并过的辅助文件remove(filename2.c_str());}for (size_t i = 0; i < v.size(); ++i){_SmallFileName.push_back(v[i]);}}//将最后合并的文件再拷贝回原文件中FILE* fWrite = fopen(_bigfile,"wb");assert(fWrite);string filename = _SmallFileName[0];FILE* fRead= fopen(filename.c_str(), "rb");assert(fRead);string line;while (_GetLine(fRead,line)){line += '\n';fputs(line.c_str(), fWrite);line.clear();}fclose(fWrite);fclose(fRead); remove(filename.c_str());             //删除辅助文件}protected:void _Merge(FILE* fRead1,FILE* fRead2,FILE* fWrite)        //将小文件进行合并{int num1 = 0;int num2 = 0;bool file1 = _GetNum(fRead1,num1);bool file2 = _GetNum(fRead2,num2);while (file1!=false&&file2!=false)          //归并排序 {if (num1<num2){_InputNum(fWrite,num1);         //向文件中写入一个数file1 =_GetNum(fRead1, num1);}else{_InputNum(fWrite, num2);file2 = _GetNum(fRead2, num2);}}while (file1==true){_InputNum(fWrite, num1);         //向文件中写入一个数file1 = _GetNum(fRead1, num1);}while(file2==true){_InputNum(fWrite, num2);         //向文件中写入一个数file2 = _GetNum(fRead2, num2);}}protected:bool _GetNum(FILE *fRead,int& num)  //读取一个整数,并将整数通过num返回{int ch = 0;string line;while ((ch = getc(fRead)) != EOF&&ch != '\n'){line += ch;}if (ch == EOF)return false;else{num = atoi(line.c_str());return true;}}bool _GetLine(FILE* fRead,string& line)   //读取一个整数,将整数以转化成字符串用line返回{int ch = 0;while ((ch = getc(fRead)) != EOF&&ch!= '\n'){line += ch;}if (ch == EOF)return false;elsereturn true;}void _InputNum(FILE *fWrite,int num)     //向文件中写一个整数,转化成字符串写入{char buf[120] = { 0 };string line;line = _itoa(num, buf, 10);line += '\n';fputs(line.c_str(), fWrite);}private:vector<string> _SmallFileName;     //存放小文件名的数组char* _bigfile;               //记录要排序的文件名size_t _limit;                //记录小文件的最多存放数据个数};





3 0
原创粉丝点击