【编程珠玑】第一章 开篇
一. 题目
如何给磁盘文件排序?
问题描述:
输入:一个最多含有n个不重复的正整数(也就是说可能含有少于n个不重复正整数)的文件,其中每个数都小于等于n,且 n=10^7。
输出:得到按从小到大升序排列的包含所有输入的整数的列表。
条件:最多有大约1MB的内存空间可用,但磁盘空间足够。且要求运行时间在5分钟以下,10秒为最佳结果。
二.分析
1)基于磁盘的归并排序(耗时间)
2)每个号码采用32位整数存储的话,1MB大约可以存储250 000 个号码,需要读取文件40趟才能把全部整数排序。(耗时间)
3)位图法,采用一个1千万位的字符串表示每个数,比如{0,2,3}表示为 1 0 1 1 0 0 0 0 。(说明:左边第一位表示 0 第二位表示1 第三位表示 2 。如果有则表示为1,否则为0)遍历每一个整数,有则标记为1,否则标记为0。然后按顺序输出每个整数。这种方法实际需要1.25MB内存,如果可以方便弄到内存的话可以采用此种方法。
4)假如严格限制为1MB,可以采用的策略:两次遍历或者除去第一位为0或1的整数。
解释:考虑到没有以数字0或1开头的电话号码,可以省略对这些数的操作。
两次遍历:对 1 ---4999 999之间的数排序,需要存储空间为:5000 000/8 =625 000 字节(8为一个字节中的位数) 对 5000 000 -10000 000 之间的数排序。 如果需要采用k趟算法,方法类似。
三. 算法实现
#include <stdio.h>
#include <stdlib.h>
#define SHIFT 5 //表示位移量
#define MASK 0x1F //掩码
#define N 10000000 //表示有1000万个数
int a[1 + N/32]; //使用整形数组来模拟定义1000万个位的数组
void set(int i) { a[i>>SHIFT] |= (1<<(i & MASK)); } //设置位数组中的从0开始的第i位为1
void clr(int i) { a[i>>SHIFT] &= ~(1<<(i & MASK)); } //设置位数组中的从0开始的第i位为0
int test(int i) { return a[i>>SHIFT] & (1<<(i & MASK)); } //取出从0开始的第i位的值,用于检测
int main()
{
int i = 0;
//int top = 1 + N/BITSPERWORD;
//memset(a, 0, sizeof(a)*sizeof(int));
for(i=0;i<N;i++)
clr(i);
while (scanf("%d", &i))
set(i);
for (i = 0; i < N; i++)
if (test(i))
printf("%d\n", i);
return 0;
}
四. 参考来源
1. http://blog.csdn.net/tianshuai11/article/details/7555563
2. http://blog.csdn.net/ju136/article/details/6832331
3. 《编程珠玑》读书笔记