算法题008 快速找出故障机器

快速找出故障机器

 

题目描述

  关心数据挖掘和搜索引擎的程序员都知道,我们需要很多的计算机来存储和处理海量数据。

  然而,计算机难免出现硬件故障而导致网络联系失败或死机。为了保证搜索引擎的服务质量,我们需要保证每份数据都有多个备份。

  简单起见,假设每个机器存储一个标号为ID的记录(ID是小于十亿的整数),假设每份数据都保存两个备份,这样就有两个机器储存了同样的数据。

  1.在某个时间,如果得到一个数据文件ID的列表,是否能够快速地找出这个表中仅出现一次的ID?

  2.如果已经知道只有一台机器死机(也就是说只有一个备份丢失)呢?如果有两台机器死机呢(假设同一个数据的两个备份不会同时丢失)?

 

我的解法

  这个题目肯定要考虑大量数据的处理,即需要重点考虑效率问题。

  首先,ID是小于十亿的整数,这说明用一个long型(4个字节)可以表示ID,不会超出表示范围,所以这点不用担心。

  根据题目特点,得到ID列表后,可以考虑一种像是玩扑克牌游戏的做法,找一个数组来盛放遍历的数(想象成摸牌),找到成对的就丢弃(或者你也可以把这个配对过程想象成连连看)。

  最后将所有数字遍历完之后还留下的就是单独的ID。

  这样时间复杂度是O(n * n),因为需要摸n张牌,并且每摸一张牌都需要和手中的牌进行查询配对。

  下面是书中的解法,看完之后才知道我的解法真是弱爆了,为什么没想到哈希表呢。

 

解法一

  直接遍历列表,利用一个数组记录下每个ID出现的次数,遍历完毕之后,出现次数小于2的ID就是我们想要的结果。

  假设有N个ID,且ID的取值在0~N-1之间,这个解法占用的时间复杂度为O(N),空间复杂度为O(N)。

  时间复杂度已经相当理想,但是空间复杂度不够理想,如果ID的数量多达几个G甚至几十G,这样的空间复杂度在实际的运算中就会带来效率问题。

 

解法二

  考虑到大部分ID的出现次数都等于2,这些ID的信息并不是必要的,所以不必存储。

  因此,可以把解法一数组中等于2的元素清空,然后用来存储下一个机器ID的出现次数,这样就可以减少需要的空间。

  具体方法如下:遍历列表,利用哈希表记下每个ID出现的次数,每次遇见一个新的ID,就向哈希表中增加一个元素;如果这个ID出现的次数为2,那么就从哈希表中删除这个元素,最后剩下的ID就是我们想要的结果。

  这个算法的空间复杂度在最好情况下可以达到O(1),在最坏的情况下仍然是O(N)。

 

解法三

  对于第一问,假设列表中仅有一个ID出现了一次,那么可以考虑用异或关系来帮忙找到结果。

  因为异或运算的定义是相同为假相异为真,异或运算满足交换律和结合律。

  所以,所有ID的异或值就等于这个仅出现一次的ID。(两两相同的都得到0,0和任何值异或等于原来的任何值)

  这种情况下,时间复杂度为O(N),空间复杂度为O(1)。

 

  对于第二问,由于有两个ID仅出现了一次,设它们为A和B,那么所有ID异或的结果是A异或B,但还是无法确定A和B的值。

  可以进行分类讨论:

  (1)A == B

  这时A异或B等于零,丢失的是同一份数据的两个拷贝,可以通过求和的方法求得A和B,即,所有ID值的和减去所有正常的ID之和,除以2得到A和B。

  (2)A !=B

  这时A异或B不等于零,那么这个异或值的二进制位中某一位为1,此时A和B中有且仅有一个数的相同位上也为1。

  我们就把所有的ID分成两类,一类在这位上为1,另一类在这位上为0。A和B分别位于这两类中。

  我们分别计算这两类的异或和,即可得到A和B的值。(太巧妙了!)

 

解法四

  对于第一问,缺失一个ID:

  预先计算并保存好所有ID的求和(“不变量”),顺序列举当前所有剩下的ID,对它们求和,然后用所有ID的求和减去当前剩下所有ID的和,结果就是死机的机器的ID值。

  时间复杂度为O(N),空间复杂度为O(1),和解法三一样是计算复杂度最优的算法。

 

  对于第二问,我们考虑所有的情况,即两个ID可以相同也可以不同。

  用上面的方法可以得到这两个ID的和。我们构造出一个方程: x + y = a; a已知。

  第二个方程有很多构造方法,比如可以用所有ID的乘积计算出另一个不变量,除以所有剩下的ID,结果得到两台死机机器的ID的乘积,即x * y = b

  这样联立两个方程之后,可以解出x和y的值。

  时间复杂度为O(N),空间复杂度为O(1)。

  第二个方程构造也可以考虑平方和的方法。

 

参考资料

  《编程之美》1.5节

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值