剑指offer面试题40:数组中只出现一次的数字

问题:一个整型数组中除了两个数字之外,其他的数字都出现了两次,写出程序找出这两个只出现了一次的数字,时间复杂度要求O(n),空间复杂度要求O(1).

一看到这个题想到了很多“可能”的解法,很飘散的思绪,比如什么哈希啊,什么加A再减去A,剩下的就是单独的了啊,都是写没啥用的解法。

后面看到了书上的解法,觉得很巧妙,于是贴了上来,顺便标明了自己当时不是很清楚的理解。

 书上说这道题很难,我觉得算法这种东西,本质上并没有难一说,而是主张一个“巧妙”,九拐十八弯。

 从易入难,举一反三是算法学习中必不可少的一种经历。

闲话少说,言归正传,首先这个题目是要求我们找出数组中的两个分别出现了一次的数字,而且时间复杂度要求O(n),如果说我们想靠找到数组中的一个A再在后面找到另一个A,这样的话,时间复杂度完全不符合要求,相应的时间复杂度为O(n^2),太。。。。慢了。。

那么怎样才能解决这个问题呢?

根据书上的提示,我们先来考虑一个比较 简单的场景,即数组中只有一个单独的不成对的数,我们可以通过连续对数组中的元素求异或(“^”操作),因为A和A的异或结果是为零的,根据这个思想,我们可以把异或的结果保存到一个result变量里,到最后这个变量的结果就是我们所求的那个单独的数。(当时困扰楼主了一会儿,让lz感觉到莫名其妙的是书上的”依次异或“这句话,楼主心想,依次异或,难不成还是依次便利数组中的元素,求异或?这样的复杂度还是O(n^2),太诡异了,现在才发现自己当时的想法是错误的,曲解了作者的想法,希望有这么理解的同学不要再这么想了~)

 

接下来考虑数组中拥有两个独立数据的情况,对这个数组中的元素同样进行上述的异或操作,得到的最终结果是两个不同的数的异或结果,由于两个数不相同,那么这个结果中肯定不为零,也就是存在1的位数,我们可以根据此结果把这个数组分成两个子数组,从低到高位,第一个1出现的位数为1的数和这位为0的数为两个数组,那么肯定的是,这两个单独的数分别被分到了两个子数组里,那么就好办了,对这两个子数组的元素分别进行异或操作,得到的就是这两个单独的数。

 

根据此思路完成的代码如下:

 

 1 #include<iostream>
 2 using namespace std;
 3 
 4 void find_two_aloneC(int a[],int length)
 5 {
 6     int i,j=1,Xor=0,Xor_0=0,Xor_1=0;
 7     
 8     for(i =0;i<length;i++)
 9         Xor = Xor^a[i];
10     while(((Xor & 1) == 0))
11     {
12         Xor = (Xor >> 1);
13         j = j << 1;
14     }
15     for(i = 0;i<length;i++)
16     {
17         if((a[i] & j) == 0)
18         {
19             Xor_0 = Xor_0^a[i];
20         }
21         else if((a[i] & j )!=0)
22         {
23             Xor_1 = Xor_1^a[i];
24         }
25     }
26     cout << Xor_0 << endl;
27     cout << Xor_1 << endl;
28 }
29 int main()
30 {
31     int a[] = {2,3,4,5,3,3,3,5,2,5};
32     find_two_aloneC(a,10);
33     system("pause");
34     
35     return 0;
36 }

 

posted @ 2013-04-16 12:43  karlthas  阅读(327)  评论(0编辑  收藏  举报