编程之美 1.5快速找出故障机器

题目:

  有很多服务器存储数据,假设一个机器仅存储一个标号为ID的记录,假设机器总量在10亿以下且ID是小于10亿的整数,假设每份数据保存两个备份,这样就有两个机器存储了同样的数据。

问题是:1.假设在某个时间得到一个数据文件ID的列表,是否能快速地找出表中仅出现一次的ID?即快速找出出现故障的机器存储的数据ID。

    2.如果有两台机器出现故障呢?(假设存储同一份数据的两台机器不会同时出现故障,即列表中缺少的是两个不等的ID)

 

给出了4种解法思路

解法一:

  最传统的比较列表,需要遍历整个列表,记录每个ID出现的次数,最后输出只出现一次的ID,时间复杂度O(n),空间复杂度也为O(n),如果数据量太大,实际运算效率会很低下。

解法二:

  优化存储空间,在很多数据中,大部分出现次数都是2,出现故障的机器为少数,通过Hash Table,Key值为机器ID,Value值为出现次数。遍历列表,遇到ID,就将ID的Value值加1。Value值为2的话,删除这个Key。最后Hash表中剩下的就是出故障的机器ID。

  最好情况下空间复杂度为O(1),最坏仍为O(n)。

解法三:

  前两种方法已经将空间复杂度降到了O(n),如果在想降到常数级,就需要换一种思路,不用遍历列表的这种方法。

如果能只用一个变量记录遍历列表的结果,那么空间复杂度可将为O(1)。

  x(N)=ID_Lost。考虑到列表中,只有一个ID出现了1次,其他都是2次,可以用到计算机语言里面的异或(XOR、⊕)。

A⊕A=0

A⊕0=A

  最终x(n)=List[0]⊕List[1]⊕List[2]⊕……⊕List[n]  的运算结果即为只出现一次的ID号,空间复杂度为1。

  但如果有2个ID只出现了一次,假设出现一次的ID为A,B,x(n)的为A⊕B,仍无法确定AB的值。

  异或之后的二进制值,某一位为1的话,可以推断出,AB2个数中,这个位置,一个是0,一个是1。我们将所有ID,分为2类,一类这位上为1,一类这位上为0。这2类个包含了AB中的一个,使用2个变量,遍历列表做异或处理,即可找出A和B的值。

解法四:

  将问题进行扩展,现在故障机器的ID相同,既2台存储相同数据的机器同时嗝屁了。运用一些数学上的知识,数学中有不变量,在这里,事先预订的整数ID集合中,所有的ID相加可以得到一个不变量将现在的ID集合相加,用不变量减去现在的ID集合的和,即可得到丢失数据ID的。时间复杂度为O(n),空间复杂度O(1)。

  如果这2台机器ID不同,我们只是得到了他们的ID之和,x+y=a,并不能知道他们到底是多少。可以再构建一个等式,构成一个方程,解方程组,得出x和y的值。同样使用不变量的概念,比如所有ID的乘积,最后得出来xy=b,解出x,y的值。

 

 

总结:

  在算法设计是一个慢慢发展过程中,开始的时候可能只是想出了最普通的一个解法,效率不高,但能解决问题。做到这一步之后,想要更进一步,就需要在时间复杂度或者空间复杂度方面做一些优化。还可以根据题中的一些条件,想一些可以用计算机方式解决的方法,如异或、与、正则表达。还可以用数学的思想来看看问题有没有解决的办法。

 

附解法一小程序

#include "stdio.h"

int main(){
    int List[]={1,2,3,5,6,7,8,9,0,1,2,3,4,5,6,8,9};//ȱ0,7,4 
    int t;
    int length=sizeof(List)/sizeof(List[0]);
    printf("%d\n",length);

    for(int i=0;i<length;i++){
    t=0;
        for(int j=0;j<length;j++)
            if(List[i]==List[j])
                t=t+1;
    if(t!=2)
        printf("%d\n",List[i]);
    }
} 

 

posted @ 2016-01-07 21:58  SeeKHit  阅读(621)  评论(0编辑  收藏  举报