技巧
例题
(1)32位整数的范围是0~(
2
32
2^{32}
232 -1),如果用字节存储,就是
2
32
2^{32}
232/8约等于500M,每个位置只有0或1,表示这个数是不是出现过
(2)进阶:
10MB约等于3kb,定义一个int[]数组,数组每一个元素占4字节,3kb/4字节,相当于每个4字节占用差不多512空间,即需要512个int,现在相当于把2^32分到512空间(512个int),即每个int分到8388608(
2
32
2^{32}
232/512).
int[]是用来做词频统计的,对于每一个数字,除以8388608可以分到int[]中不同位置,即对应位置词频++,40亿个数字分完后,一定有一个区间不够8388608
定位到某个范围后,重复上面的过程,直到定位到对应的数字
(3)变形:能不能使用更少的空间
不断二分
2.
哈希表统计词频,词是对应的hash key
多个小文件大根堆,然后把每个小根堆堆顶放到一个总的大根堆中,此时可以弹出甲,然后第二个小文件大根堆再放一个丙到总堆,重复操作
3.
法一:1G内存八字节,可以知道分多个小文件,使用hash函数分流,把无符号整数分到多个小文件,然后统计
法二:用两个bit表示一个数字出现的状态,11表示出现两次以上
补充:
使用题目1的int[]不断统计
-
一个10G文件,存储的无符号int,现在变成一个新文件,有序且只有5G内存,怎么实现
解:
组织一个小根堆,存储记录包括存储的内容和词频,即一条记录8字节,考虑到可能有一些额外损耗,我们假设一条记录16字节,根据词频组织小根堆。
5G/16字节,大概支持 2 27 2^{27} 227条记录,将 2 32 2^{32} 232的数字(无符号整数的总量)分到 2 27 2^{27} 227记录中,即每个记录中应该表示2^5个数字。 -
n^1表示反转(n只能是0或1)
上述的方法可能存在a-b溢出的情况
改进:
是2的幂表示只有一个1
法1:
A&(~A+1)获得二进制最右侧的1
法2:
x&(x-1)==0
是4的幂表示只有一个1且在偶数位
先判断x是不是只有一个1,即是不是2的幂,
然后:
如果等于0则不是4的幂
0x5555555就是32位0101…01
7.
-
加法
异或:无进位相加
与运算左移一位:进位信息
两者相加即实现了加法,但由于不可以使用加法,因此不断重复两个数求异或和与运算左移一位的结果,最后当其无进位相加信息是000000…时,与的结果就是加法的结果
-
减法
第二个数变成加法的相反数
-
乘法
-
除法
乘法的逆运算
选择(x>>i)>=y而不是x>(y<<i),因为左移可能溢出