这个CSDN上看到的腾讯面试题,总算是搞清楚了!
以下转:
一个文件中有40亿个整数,每个整数为四个字节,内存为1GB,写出一个算法:求出这个文件里的整数里不包含的一个整数
4个字节表示的整数,总共只有2^32约等于4G个可能。
为了简单起见,可以假设都是无符号整数。
分配500MB内存,每一bit代表一个整数,刚好可以表示完4个字节的整数,初始值为0。基本思想每读入一个数,就把它对应的bit位置为1,处理完40G个数后,对500M的内存遍历,找出一个bit为0的位,输出对应的整数就是未出现的。
算法流程:
1)分配500MB内存buf,初始化为0
2)unsigned int x=0x1;
for each int j in file
buf=buf|x < <j;
end
(3) for(unsigned int i=0; i <= 0xffffffff; i++)
if (!(buf & x < <i))
{
output(i);
break;
}
以上只是针对无符号的,有符号的整数可以依此类推。
该题有两种比较优良的算法。。其中一种就是楼上所述的位图操作。。另一种就是使用很少的内存,预料不到的二分法,因为不存在的数必然在最大数和最小数及中间数的两个范围里,必然较小的范围里,通过不断缩短范围,就可以求出来。。。。
这里只给出第一种算法。。
详细见《编程珠玑》
位图运算
/* Copyright (C) 1999 Lucent Technologies */
/* From 'Programming Pearls' by Jon Bentley */
/* bitsort.c -- bitmap sort from Column 1 * Sort distinct integers in the range [0..N-1] */
#include <stdio.h>
#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
#define N 10000000
int a[1 + N/BITSPERWORD];
void set(int i) { a[i>>SHIFT] |= (1 < <(i & MASK)); }
void clr(int i) { a[i>>SHIFT] &= ~(1 < <(i & MASK)); }
int test(int i){ return a[i>>SHIFT] & (1 < <(i & MASK)); }
int main(){
int i;
for (i = 0; i < N; i++)
clr(i);
while (scanf("%d", &i) != EOF)
set(i);
for (i = 0; i < N; i++)
if (test(i))
printf("%d\n", i);
return 0;
}
====================================================
本文是在
http://blog.csdn.net/king821221/archive/2008/01/20/2054353.aspx
结合自己总结完成的
感谢csdn 感谢...
案例1 :假设一个文件中有9亿条不重复的9位整数,现在要求对这个文件进行排序。
方法:bit 位操作
首先
32位机的寻址能力是 2的32次方, 即4G.寻址能力最大是这样了.
一个最大的9位整数为999999999
这9亿条数据是不重复的
声明一个bit数组,长度为10亿
一共需要10亿 / 8 / 1024 /1024 =120M
把内存中的数据全部初始化为0
读取文件中的数据,并将数据放入内存。比如读到一个数据为341245909这个数据,那就先在内存中找到341245909这个bit,并将bit值置为1
遍历整个bit数组,将bit为1的数组下标存入文件
然后再遍历一次,找出不为零的就可以了.
注意:
如果是有重复的数字,
需要考虑重复的情况
如果普遍多于2个,那么就要设置包含2个bit的数组
或者使用bitmap类型.
案例2
最快的方法找出一篇文章中各个字符的出现次数
声明数组int[256]
以该字符的aci码作为下标即可
案例3 全排列
a,b,c ,d, e 的所有排列
令a =1 ,b = 2,c = 3,...
那么从12345到54321的所有数打印出来就可以了
如果有别的限制,比如ac不相邻
那么就再转换成字符串判断好了.
案例4 组合
a b c d e 5个字母中选3个的组合
可以使用
a b c d e ----- 数组R(i)
a b c d e ------数组S(j)
a b c d e -------数组T(k)
模拟5进制的加法,R,S,T下标从0开始,然后T下标加一,加到5的话就让s下标加一
还有,既然是组合,就要保证s的下标从R的开始,T的从S的开始
for ( i = 0 ;i< 5;i ++)
for (j =i ;j< 5;j++)
for (k = j;k<5;k++)
...{
//打印R(i)S(j)T(k)
}
案例5 把m个球分n组
0 1 2 ... m
...
0 1 2 ... m
总共n行
模拟M进制的加法即可
(其实和回溯法一个原理,只是这个更好理解)
案例6 换零钱问题
有足够的 1元,5元,10元,20元硬币
组合成170元有多少方法?
其实就是解方程
1 * x1 + 5 * x2 + 10 * x3 + 20 * x4 = 170
然后试解循环法解 x1,x2,x3,x4
案例6 一个文件中有40亿个整数,每个整数为四个字节,内存为1GB,写出一个算法:求出这个文件里的整数里不包含的一个整数
:如果采用bit数组,40亿个bit也就120*4 = 480M 的空间,所以1G绰绰有余.
而40亿的整数占用的是480M * 32 = 15360M 即大约15G, 问题是怎样把这数字读进来
可以一个一个的读.
另外csdn上有个方法,不知道在这里有什么帮助,不过原理还是不错:
使用分段的方法:
因为4字节的整数可以用16进制表示为
00000000H 到 FFFFFFFFH
我们可以分成1000H为一段,把整数分割,
统计每个区间的占有情况
如果是1000H.那么统计不包含的数字就略过这个区间好了
但是这个前提还是要读进来吧?