问题:假设一个机器仅仅存储一个标号为ID的记录(ID小于10亿的整数),每个数据保存2个备份,这样就有2个机器存储了同样的数据。
提问1:某时间如果得到一个数据文件ID的列表,是否能够快速地找出这个表中仅仅出现一次的ID?
提问2:如果已经知道只有一台机器死机(即只有一个备份丢失)呢?如果有2台机器死机(设同一个数据的两个备份不会同时丢失)?
解提问1转换为有很多ID,其中只有一个ID出现次数小于2,其他正常ID出现次数等于2,如何找到这个次数为1的ID。
解法一 遍历计数
思路:遍历ID列表,用一个数组记下每个ID出现的次数,遍历完毕后出现次数小于2的就是所需结果。
时间复杂度为O(N),空间复杂度为O(N)。
缺点:当ID数量多达即G时,空间复杂度效率明显出现问题,能否减小空间复杂度,对于出现2次的ID可以删除,因为已经不符合条件。解法二就是这个思路。
解法二 遍历列表对于出现次数为2 的ID不存储,
利用hash表记下每一个ID的出现次数,遇见一个ID向hash表中增加一个元素,如果ID的出现次数为2,则将其从hash表中删除,最后剩下的就是所需ID。
算法空间复杂度为最好为O(1),最差仍为O(N)。
解法三 异或运算
试图进一步降低空间复杂度,若要继续降低空间复杂度就有摒弃遍历列表计数这种方法。将列表中的ID进行异或由于问题一中除所求ID外都出现2次,故对列表中的ID进行异或后得到的结果就是出现次数为1的ID。
缺点:上述针对只有一个ID出现次数为1的情况,出现多次则不可行。
解提问2 有2个ID(设为A,B )仅仅出现一次,解法一二适用,但解法三所有ID的异或值为A⊕B,无法确定A,B的值。
书中分情况讨论
② A=B时,则A=B=(所有ID之和—所有正常工作机器ID之和)/2。
②若A!=B则A⊕B不为0,但异或值的某一位为1,那么A和B的相同位上也为1,将ID列表根据该位分为2组,一组该位为1,另一组该位为0,每一类中分别含有A和B,使用两个变量遍历列表时分别计算这两类ID的异或值,即可得到AB的值(因为对应组别中仅仅A或者B出现一次,其他ID 都出现2次)。
解法四 利用不变量
针对提问一 将所有ID求和与死机后的ID列表和相减即为死机机器所存ID
针对第二问 2个ID不同的情形
(1) 计算未丢失之前所有ID和,计算丢失ID之后的和
(2) 将上述2和相减得到A+B=P;
(3) 利用丢失前后的ID平方和之差与(2)联立求解AB
书中第二个方程利用丢失前后ID的乘积求得A*B的值,也提出乘积会导致溢出建议采用平方和。
扩展问题 3个备份的时候,可有和,平方和,三者乘积得到3个方程,N个时会没有足够的方程无法求解。
扑克牌问题 未抽取牌前的和减去抽取牌后的和即可。全部异或也可。
哈希表 归并排序,快速排序