27、请定义一个宏,比较两个数 a、b 的大小,不能使用大于、小于、if 语句 

28、两个数相乘,小数点后位数没有限制,请写一个高精度算法 

29、有 A、B 、C、D 四个人,要在夜里过一座桥。他们通过这座桥分别需要耗时 1、2、5、

10 分钟,只有一支手电,并且同时最多只能两个人一起过桥。请问,如何安排,能够在 17
分钟内这四个人都过桥? 

30、有 12 个小球,外形相同,其中一个小球的质量与其他 11 个不同,
给一个天平,问如何用 3 次把这个小球找出来,并且求出这个小球是比其他的轻还是重

31、在一个文件中有  10G  个整数,乱序排列,要求找出中位数。内存限制为  2G。只写出
思路即可。

32、一个文件中有 40 亿个整数,每个整数为四个字节,内存为 1GB,写出一个算法:求出
这个文件里的整数里不包含的一个整数

33、腾讯服务器每秒有 2w 个 QQ 号同时上线,找出 5min 内重新登入的 qq 号并打印出来。


解答:

27、请定义一个宏,比较两个数 a、b 的大小,不能使用大于、小于、if 语句 

如果是整型: 
#define MAX(a,b) (abs((a)-(b))==((a)-(b))?(a):(b))
如果是浮点数:
将abs()函数换成fasb()函数,fabs()所接受的参数及返回值都是double型的
头文件math.h

28、两个数相乘,小数点后位数没有限制,请写一个高精度算法 

其实就是大数乘法,字符串读入
首先考虑小数点的位置,去掉之后相乘,输出时加上 

    输入 字符串 a b;计算c=a*b; 返回 c;
1,纪录小数点在a,b中的位置l1,l2,则需要小数点后移动位置数为
l=length(a)+length(b)-l1-l2-2;
2,    去掉a,b中的小数点(a,b小数点后移,使a,b变为整数)
3,    计算c=a*b; (同整数的大数相乘算法)
4,    输出c,(注意在输出倒数第l个数时,输出一个小数点。若是输出的数少于l个,就补0)

29、有 A、B 、C、D 四个人,要在夜里过一座桥。他们通过这座桥分别需要耗时 1、2、5、10 分钟,只有一支手电,并且同时最多只能两个人一起过桥。请问,如何安排,能够在 17分钟内这四个人都过桥? 

这里想办法 让相近的人一起 让短时间的人来回 1 2一起 5 10一起  
第一趟过去:两人:花1分钟和2分钟的人  //总共耗时:2分钟
第一趟过来:一人:花1分钟的人         //总共耗时:3分钟
第二趟过去:两人:花5分钟和10分钟的人 //总共耗时:13分钟
第二趟过来:一人:花2分钟的人         //总共耗时:15分钟
第三趟过去:两人:花1分钟和2分钟的人  //总共耗时:17分钟

30、有 12 个小球,外形相同,其中一个小球的质量与其他 11 个不同,给一个天平,问如何用 3 次把这个小球找出来,并且求出这个小球是比其他的轻还是重

分三组:每组四个,第一组编号1-4,第二组5-8,第三组9-12.  

第一次称:天平左边放第一组,右边放第二组。  
    A 第一种可能:平衡。则不同的在第三组。  
        接下来可以在左边放第9、10、11号,右边放1、2、3号三个正常的。  
            a.如果平衡,则12号是不同的;  
            b.如果左重右轻,则不同的在9、10、11号中,而且比正常球重。 
                再称一次:9放左边,10放右边,如果平衡,则11号是不同的;如果左重右轻,则9号是不同的,如果右重左轻,则10号是不同的。  
            c.如果左轻右重,道理同b  

B 第二种可能:左重右轻,则不同的在1-8号中,但不知比正常的轻还是重。  
    第二次称:左边放1、2、5号,右边放6、9、3号。  
        a.如果平衡。则不同的在4、7、8中。 
            可以称第三次:左边放4、7,右边放9、10。如果平衡,则8是不同;如果左重右轻,则4是不同;如果左轻右重,则7是不同。  
        b.仍然左重右轻。则不同的在位置没有改变的1、2、6中。 
            可以称第三次:左边放1、6,右边放9、10。如果平衡,则2是不同; 如果左重右轻,则1是不同;如果左轻右重,则6是不同。  
        c:左轻右重。则不同的在5、3、中,因为只有它们改变了原来的位置。 
            可以称第三次:左放5,3,右放9,10。如果左轻右重,则5是不同,如果左重右轻,则3是不同。  
C 第三种可能:左轻右重,道理同B  
至此,不论发生任何情况,称三次都可以找出不同,而且知道比正常的轻了还是重了。 
 
13个球也是可以做的。就是分成4个、4个、5个, 
先拿两个四个上去,如果平衡,则问题出在5个那组, 
就在5个里任拿三个设为C1C2C3,再拿三个正常的,分别放两边,若平衡就简单啦, 
若不平衡,就出现C1C2C3重,或C1C2C3轻,相当于就知道那个特别的球是比较重或者比较轻啦,接下就不用说了  
 
如果不平衡,假设现在是A重B轻,  
    取A1+A2+B1放天平一边(设为左边), 再取A3+A4+B2放另一边(右),  
    若平衡,就在B3/B4任拿一个跟C1上去称就行了,  
   如果不平衡,那么假设  
   情况一:左重 则是A1/A2/B2有问题 直接把A1A2放两边称,重的那个有问题,如果平衡就是B2有问题  
   情况二:右重 就是 A3/A4/B1有问题,方法同上 

31、在一个文件中有10G个整数,乱序排列,要求找出中位数。内存限制为2G。只写出思路即可。

关于中位数:数据排序后,位置在最中间的数值。
即将数据分成两部分,一部分大于该数值,一部分小于该数值。
中位数的位置:当样本数为奇数时,中位数=(N+1)/2 ; 
当样本数为偶数时,中位数为N/2与1+N/2的均值(那么10G个数的中位数,就第5G大的数与第5G+1大的数的均值了)。

1. 原数据不能读进内存,不然可以用快速选择,如果数的范围合适的话还可以考虑桶排序或者计数排序,
但这里假设是32位整数,仍有4G种取值,需要一个16G大小的数组来计数。
2. 若看成从N个数中找出第K大的数,如果K个数可以读进内存,可以利用最小或最大堆,
但这里K=N/2,有5G个数,仍然不能读进内存。
3. 接上,对于N个数和K个数都不能一次读进内存的情况,《编程之美》里给出一个方案:
设k<K,且k个数可以完全读进内存,那么先构建k个数的堆,先找出第0到k大的数,
再扫描一遍数组找出第k+1到2k的数,再扫描直到找出第K个数。
虽然每次时间大约是nlog(k),但需要扫描ceil(K/k) 次,这里要扫描5次。

解法:首先假设是32位无符号整数。
1. 读一遍10G个整数,把整数映射到256M个区段中,用一个64位无符号整数给每个相应区段记数。
说明:整数范围是0-2^32 - 1,一共有4G种取值,映射到256M个区段,
则每个区段有16(4G/256M = 16)种值,每16个值算一段, 0~15是第1段,16~31是第2段,……2^32-16 ~2^32-1是第256M段。
一个64位无符号整数最大值是0~8G-1,这里先不考虑溢出的情况。总共占用内存256M×8B=2GB。
2. 从前到后对每一段的计数累加,当累加的和超过5G时停止,
找出这个区段(即累加停止时达到的区段,也是中位数所在的区段)的数值范围,
设为[a,a+15],同时记录累加到前一个区段的总数,设为m。
然后,释放除这个区段占用的内存。
3. 再读一遍10G个整数,把在[a,a+15]内的每个值计数,即有16个计数。
4. 对新的计数依次累加,每次的和设为n,当m+n的值超过5G时停止,此时的这个计数所对应的数就是中位数。

总结:
1.以上方法只要读两遍整数,对每个整数也只是常数时间的操作,总体来说是线性时间。
2. 考虑其他情况。
若是有符号的整数,只需改变映射即可。若是64为整数,则增加每个区段的范围,那么在第二次读数时,
要考虑更多的计数。若过某个计数溢出,那么可认定所在的区段或代表整数为所求,这 里只需做好相应的处理。
噢,忘了还要找第5G+1大的数了,相信有了以上的成果,找到这个数也不难了吧。
3. 时空权衡。
花费256个区段也许只是恰好配合2GB的内存(其实也不是,呵呵)。可以增大区段范围,
减少区段数目,节省一些内存,虽然增加第二部分的对单个数值的计数,
但第一部分对每个区段的计数加快了(总体改变??待测)。
4. 映射时尽量用位操作,由于每个区段的起点都是2的整数幂,映射起来也很方便。


32、一个文件中有 40亿个整数,每个整数为四个字节,内存为 1GB,写出一个算法:求出这个文件里的整数里不包含的一个整数

4个字节表示的整数,最大可表示为2^32=4294967296,约等于4G个可能,
建立一个bit map,共需要4294967296个bits(也就是0.5G字节的内存分配500MB内存,每一bit代表一个整数,刚好可以表示完4个字节的整数,
初始值为0。基本思想每读入一个数,就把它对应的bit位置为1,
处理完40G个数后,对500M的内存遍历,找出一个bit为0的位,输出对应的整数就是未出现的。

33、腾讯服务器每秒有2w个 QQ号同时上线,找出5min内重新登入的qq号并打印出来

1.位图 
 5分钟内登陆的QQ数为5*60*2W = 6M。也是用位图,只不过是用两个位表示一个数,
0表示未登录,1表示登陆一次,2表示登陆多次。

2直接用STL的set。从某一时刻开始计时,每登陆一个QQ,把它放入set,如果已存则直接打印。
直到5min后,就可以over了。
      空间复杂度:用str存储每个QQ号,假设QQ号有20位,理想情况下每个QQ占20Byte。
则5min内的QQ:2w * 60 * 5 = 600w个,需要的存储空间600w * 20byte = 12000w byte = 120M,
这样的存储应该可以忍受吧。
      时间复杂度:STL的set是用二叉树(更确切的说是:红黑树)实现的,查找效率是O( lgn ),
     呃,有人说不让用STL。
这里用一个trie tree吧。节点内容包括QQ号、指向子节点的指针(这里有10个,认为QQ由0---9的数字组成)。
登陆时间要不要?考虑这样一个问题:是否需要把所有的QQ都保存在内存中?随着时间的增加,登陆的QQ会越来越多,比较好的方法是把长时间不登陆的QQ释放掉。所以需要记录登陆时间,以便于释放长期不登陆的QQ。