这是《编程珠玑》中的一道题目。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");
/* 生成m个小于等于n的随机数 */
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;
/* child 是 i 的左儿子 */
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){
/* 前100万个数构建最小堆 */
if(count < HEAPSIZE){
++count;
num[count] = number;
siftup(count);
continue;
}
/* 从100万零1个数开始,将当前数与根元素比较 */
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函数保持最小堆性质。
(结束)