这是《编程珠玑》中的一道题目。10亿个整数,假设每个整数需要四个字节,如果使用排序的话,需要大约4G的内存,现在的很多pc都没有多这么内存,更不用说作者那个年代。
我们借助最小堆来解决这个问题。
主要步骤
- 使用一个大小为一百万零一的整数数组来构建堆(堆的下标从1开始)
- 从文件中读取前一百万个数,每读入一个数,调用函数,保持其最小堆的性质,堆的根永远是堆中最小的元素。
- 从一百万零一个数开始,每读入一个数开始,比较这个数与堆的根比较,如果比根大,就用这个数替换掉根,调用函数,保持最小堆性质。
先生成10亿个随机数,使用的也是《编程珠玑》上的方法,将这10亿个数输出到data.txt中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| #include <stdlib.h> #include <stdio.h>
int main(int argc, char *argv[]) { unsigned m, n; printf ("生成m个小于等于n的随机数!,请输入m和n:\n"); scanf("%d%d", &m, &n);
FILE *fp = fopen("data.txt", "w"); unsigned i; for(i = 0; i < n; ++ i){ int temp = rand(); if((temp % (n - i)) < m){ fprintf(fp, "%d\n", i); -- m; } } fclose(fp);
return 0; }
|
生成了10亿个小于20亿的随机数,并且这10亿个数是不重复的。整个data.txt文件大约有10G,我的系统无法打开,内存不够。
然后就是从这10亿个数中选出前100万个最大的数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
| #include <stdio.h> #include <stdlib.h>
#define HEAPSIZE 1000000
unsigned num[HEAPSIZE+1];
void siftup(int size){ int i = size; unsigned parent, temp;
while(1){ if(i == 1) break;
parent = i / 2; if(num[parent] <= num[i]) break;
temp = num[parent]; num[parent] = num[i]; num[i] = temp;
i = parent; } }
void siftdown(int size){ int i = 1; unsigned child;
while(1){ child = 2 * i; if(child > size) break;
if(child + 1 <= size){ if(num[child+1] < num[child]){ ++child; } }
if(num[i] <= num[child]) break;
unsigned temp; temp = num[i]; num[i] = num[child]; num[child] = temp;
i = child; } }
int main(int argc, char *argv[]) {
FILE *fp = fopen("data.txt", "r");
unsigned i, number; unsigned count = 0; while(fscanf(fp, "%d", &number) != EOF){ if(count < HEAPSIZE){ ++count; num[count] = number; siftup(count); continue; }
if(number > num[1]){ num[1] = number; siftdown(count); } } fclose(fp);
FILE *result_fp = fopen("result.txt", "w");
for(i = 1; i <= HEAPSIZE; ++i){ fprintf(result_fp, "%d\n", num[i]); } fclose(result_fp);
return 0; }
|
前100万个数使用siftup函数构建最小堆,而后的数与堆的根比较,如果比根大,则替换掉根,并使用siftdown函数保持最小堆性质。
(结束)