一道经典的面试题目(寻找1-1000中重复的数字)

        网上流传这样一道面试题目,打着“微软面试题目”的名堂,题目大意如下:

        1-100的整数中少了一个数字,且剩余的99个数是乱序,我们可以想象成一个具有99个元素的乱序数组,数组元素在1-100之间且各不相同,要求找出那个

缺少的数字。How ?考虑时间和空间的最优(最少)代价。

       浙大研究生面试过程据说导师同样问过这道题目,学生答:1-100的总和减去数组和就是啦,导师:慢了点,呵呵。。。

       这里不讨论这道题目,讨论一道与之相似的题目,是我这几天在CSDN一位同仁的博客上见到的一道题目,在这里做一个总结,自己学习一下,因为有总结才会有提高,呵呵。。。

     

    题:假设你有一个用1001个整数组成的数组,这些整数是任意排列(乱序)的,但是你知道所有的整数都在1到1000(包括1000)之间。此外,除一个数字出现两次外,其他所有数字只出现一次。假设你只能对这个数组做一次处理,用一种算法找出重复的那个数字。如果你在运算中使用了辅助的存储方式,那么你能找到不用这种方式的算法吗?

  

   算法一:

   一个显而易见的做法是我们把1-1000的和s1算出来,再把数组1001个元素的和s2计算出来,那么s2-s1就是我们想要找到的那个重复的数字。

   这种做法,一般人很少看做“算法”,其实不然,数学的美在于从简单的运算一步步演化为复杂的运算,经典的算法往往都是从“蛮力”算法一步步优化得来的。

   算法的实现代码可以如下:

int Fun_1(const int a[])
{
  int s1=0,s2=0;
  s1 = 1001*1000/2; 
  for(int i = 0; i<1001; i++)
    {
      s2+=a[i];
    }
  return s2-s1;
}

 

           分析:算法的思路很清晰,在时间复杂度上是O(n),空间复杂度上近似为S(1)。可以想象的是,无论如何,数组的所有元素都必须遍历一遍,也即在任何条件下,无优化可循。

   与之同道理的是我们可以运用XOR的做法,当然以下给出一个简单的证明过程:

       假设重复的数字为A,剩余的数字异或的结果为B,那么a数组中的所有元素异或结果result1为A^A^B,而1-1000中所有数字的异或结果result2为A^B;

       那么我们再做运算result1^result2结果如下:

   result1^result2=(A^A^B)^(A^B)

                  =B^A^B(由交换律)

                  =A.

       而A恰恰是我们需要找到的数字,算法在实质上与上面的是一样的,异或运算可以解释为“寻找不同”,在微观上是按位运算的,然而推及到宏观上也同样适用,我们的做法相当于找出两个数组(a数组和1-1000组成的数组)的“不同“,这个“不同”,显而易见就是多出来的那个重复元素A。在时间和空间效率上其实比上述的加法运算好不了多少,唯一可以“称道”的可能只是位运算省去了进位,在微观上的运算能快一点。

     算法二:空间换效率

       有了“空间换效率”的标题,相信好多人都已经想到了如何解。我们做一个数组b[1000],用作hash表,用1-1000的数组作为索引,hash表中存放数字出现的次数,这样我们依旧按照顺序建立索引,存储一并计算,遇到某一个hash元素值为2时就可以结束了。

  算法的实现代码可以如下:

int Fun_2(const int a[])
{
  int hash[1001]={0};

  for(int i = 0; i<1001; i++)
  {
    if((++hash[a[i]]) == 2)

       break;
  }
  return a[i];
}


       分析:算法在实现上借用了hash的概念,当然没有必要去复杂化概念,因为实现上无非还是做了一个数组。时间复杂度依旧是O(n),空间复杂度上升为S(n),但细分析,算法在平均时间效率上却显而易见地超过了算法一,平均算来可能只遍历到数组的一半,这就是我们常说的“空间换时间”,做了一个时间上的优化。

从通用性上讲这种hash的做法通用性更好,我们可以假设数组元素并非在1-1000之间,当然可能我们需要更多的空间来做hash统计。

   算法三:空间上的优化

       有了算法二,很多人会想,我们可以对空间再做优化吗?How?

       仔细思考确实有优化的途径,不过要针对特定的情况,比如本题目,而非通用的。实际编程中也常常都是针对特殊情况来优化的。

   以下是优化的一种方法:

   优化:

#define IMPOSSIBLE_VALUE -1
template 
T& find(T src[],const int& len)
{
    for(int i=0;i
 
 

 分析:空间优化的目的达到了,但却改变了原来的数组,从信息论的角度上讲,因为存在信息丢失,这是很多人无法接受的。而在时间上若从微观上看,内存Cache的命中率肯定会降低不少。  

其他优化方案愿共同探讨!

  欢迎批评指正!

你可能感兴趣的:(Algorithm,and,Data,Structure,面试,算法,优化,fun,存储,cache)