《编程之美》笔记1 寻找缺失的数

 

  首先,该题中一个机器中只有一个记录,如果机器出故障,就缺失了该ID记录的一个备份。

  这个问题实际上与在10亿个数中找出只出现零次,一次,两次,N次......的问题相同。(在《编程珠玑》第二章中遇到过寻找在文件中出现过0次的数的问题)

  并且我们知道有如下假设:1)10亿个ID一定每个都出现过;2)大部分ID的出现次数与备份数有关;3)小部分出现0次或1次的ID一定就是该ID对应的机器出现了故障

 

解法一:普通的遍历搜索方法。

  使用一个10亿长的数组 a[N],遍历得到的ID列表,出现了一次ID,就将对应的a[ID]++,最后值为1的项就是要寻找的ID。——时间复杂度和空间复杂度均为O(N)。

  继续考虑:1.如果确定只有一个ID出现次数为1,其他均为2,就可以使用bool数组,进而使用位图结构bitmap。

       2.如果对于第二问,如果有两个数据项丢失,可能出现同一ID出现0次,或 两个ID分别出现一次。要区分0次,1次,2次,就至少需要2bit,进而可以使用修改过的bitmap结构(每两位表示一个ID)。

 

解法二:使用hash表(set集)

  该方法的前提是解法一的第一种情况。

  由于大部分数据出现两次。可以使用一个Hash表来存储那些出现过一次的ID,也就是遍历ID列表,对当前某个ID,如果在Hash表中没有出现,就加入(这是第一次出现)。如果在Hash表中已有该ID,就删除这个ID,遍历下一个(说明这个ID出现了两次,是正常的ID)。

  最后,Hash中剩下的那个ID就是出现了一次的ID。

 

解法三:使用异或

  解法三比较新颖。因为从解一和解二可以看到,如果采用遍历存储状态的方法,空间复杂度最坏一定会达到O(N)。

  使用异或方法基于这个事实: X ^ X = 0 , X ^ 0 = X; (^表示位异或)

  所以对于出现了两次的ID,其异或结果一定为0,0再与出现了一次的ID异或,结果就为该目标ID。(因为异或支持交换律和结合律)

  对于有两个数据项出现缺失的情况,设为A和B,

  若A = B,就是说,丢失了同一个数据的两份拷贝(同一ID出现0次),结果A ^ B = 0,结果不能找到这个ID。

  若A != B,有两个ID出现了一次,A^B结果不为0,结果的二进制数一定有某位为1,以该位作为划分标准,可以将所有ID分成两类:该位为1,该位0。那A和B肯定在不同的类别中。使用两个变量,在遍历一遍列表,分别计算这两类ID的异或和,可以分别得到A和B的值。

 

解法四:使用"不变量“

  前面的解法中一个普遍问题就是处理不了 两台故障机器ID相同的情况。

  使用“不变量”的概念,所有ID的和是一个定值(“不变量”),设为SUM,这个可以预先计算出来。

  将现有列表中所有ID相加,得到M,如果只丢失了一个数,就可以使用SUM-M直接计算出这个ID。

  如果丢失了两个数,得到的就是 x+y = a;要计算这个方程式中的x和y,就在需要其他条件。可以采用 xy = b;或 x2+y2 = c; 这样就能求解方程组。

 

对于扩展的问题,有N个备份,N台机器死机,使用解法一,(修改的bitmap结构)

类似的问题是从一副扑克牌中抽出一张牌,根据剩余的牌分析抽出牌的点数。

posted @ 2012-09-12 12:30  dandingyy  阅读(440)  评论(0编辑  收藏  举报