给海量数据进行排序
本文参照:作者:July,yansha,5,编程艺术室。
出处:http://blog.csdn.net/v_JULY_v 。
如何给10^7个不重复的数据量的磁盘文件排序?
2种解决方案。
1、归并排序。你可能会想到把磁盘文件进行归并排序,但题目要求你只有1MB的内存空间可用,所以,归并排序这个方法不行。
2、位图方案。熟悉位图的朋友可能会想到用位图来表示这个文件集合。例如正如编程珠玑一书上所述,用一个20位长的字符串来表示一个所有元素都小于20的简单的非负整数集合,边框用如下字符串来表示集合{1,2,3,5,8,13}:
0 1 1 1 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0
上述集合中各数对应的位置则置1,没有对应的数的位置则置0。
首先尝试用位图方案:
但是经过分析发现如果只用一次那么需要的内存大于1M,但是如果分两次那么每次只需0.65M。
对文件进行第一次扫描,如果小于5000000则进行位图表示,写入文件。
对文件进行第二次扫描,如果大于5000000则进行位图表示,写入文件。
耗时6s。
//copyright@ yansha //July、2010.05.30。 //位图方案解决10^7个数据量的文件的排序问题 //如果有重复的数据,那么只能显示其中一个 其他的将被忽略 #include <iostream> #include <bitset> #include <assert.h> #include <time.h> using namespace std; const int max_each_scan = 5000000; int main() { clock_t begin = clock(); bitset<max_each_scan> bit_map; bit_map.reset(); // open the file with the unsorted data FILE *fp_unsort_file = fopen("data.txt", "r"); assert(fp_unsort_file); int num; // the first time scan to sort the data between 0 - 4999999 while (fscanf(fp_unsort_file, "%d ", &num) != EOF) { if (num < max_each_scan) bit_map.set(num, 1); } FILE *fp_sort_file = fopen("sort.txt", "w"); assert(fp_sort_file); int i; // write the sorted data into file for (i = 0; i < max_each_scan; i++) { if (bit_map[i] == 1) fprintf(fp_sort_file, "%d ", i); } // the second time scan to sort the data between 5000000 - 9999999 int result = fseek(fp_unsort_file, 0, SEEK_SET); if (result) cout << "fseek failed!" << endl; else { bit_map.reset(); while (fscanf(fp_unsort_file, "%d ", &num) != EOF) { if (num >= max_each_scan && num < 10000000) { num -= max_each_scan; bit_map.set(num, 1); } } for (i = 0; i < max_each_scan; i++) { if (bit_map[i] == 1) fprintf(fp_sort_file, "%d ", i + max_each_scan); } } clock_t end = clock(); cout<<"用位图的方法,耗时:"<<endl; cout << (end - begin) / CLK_TCK << "s" << endl; fclose(fp_sort_file); fclose(fp_unsort_file); return 0; }
接下来采用多路归并来解决。
源文件大约是40M,所以把源文件分成40分,每份通过快排得到有序,之后对40份有序文件进行归并排序。
声明一个40个大小的临时数据,一次存入每个文件的头一个数字,然后通过最小堆,把第一个堆里的数字存入文件,然后从那个数字所处的文件中读入下一个数字,直到文件都读完。
大约耗时20s,主要是磁盘的读写很耗时。
浙公网安备 33010602011771号