程序员面试题精选100题(34)-数组中只出现一次的数字[算法]【还需再研究】

转自: http://blog.csdn.net/hackbuteer1/article/details/6889217

有N+2个数,N个数出现了偶数次,2个数出现了奇数次(这两个数不相等),问用O(1)的空间复杂度,找出这两个数,不需要知道具体位置,只需要知道这两个值。
       求解:【任何一个数字异或它自己都等于0,而0与任何数字求异或,都为原数字!

如果只有一个数出现过奇数次,这个就比较好求解了,直接将数组中的元素进行异或,异或的结果就是只出现过奇数次的那个数。
      但是题目中有2个数出现了奇数次?,求解方法如下:
      假设这两个数为a,b,将数组中所有元素异或结果x=a^b,判断x中位为1的位数(注:因为a!=b,所以x!=0,我们只需知道某一个位为1的位数k,例如0010 1100,我们可取k=2或者3,或者5),然后将x与数组中第k位为1的数进行异或,异或结果就是a,b中一个,然后用x异或,就可以求出另外一个。
      为什么呢?因为x中第k位为1表示a或b中有一个数的第k位也为1,假设为a,我们将x与数组中第k位为1的数进行异或时,也就是将x与a外加上其他第k位为1的出现过偶数次的数进行异或,化简即为x与a异或,结果是b。
     代码如下:

 

[cpp]   view plain copy
  1. void getNum(int a[],int length)  
  2. {  
  3.     int s = 0;//保存异或结果  
  4.     for(int i=0;i<length;i++)  
  5.     {  
  6.         s=s^a[i];  
  7.     }  
  8.     int temp1 = s;     //临时保存异或结果  
  9.     int temp2 = s;     //临时保存异或结果  
  10.     int k=0;  
  11.     while((temp1&1) == 0)        //求异或结果中位为1的位数  
  12.     {  
  13.         temp1 = temp1>>1;  
  14.         k++;  
  15.     }  
  16.     for(int i=0;i<length;i++)  
  17.     {  
  18.         if((a[i]>>k)&1)          //将s与数组中第k位为1的数进行异或,求得其中一个数  
  19.         {  
  20.             //cout<<a[i]<<" ";  
  21.             s=s^a[i];  
  22.         }  
  23.     }  
  24.     cout<<s<<" "<<(s^temp2)<<endl;             //(s^temp2)用来求另外一个数  
  25. }  

 

找出数组中只出现一次的3个数。

思路类似于求解上题,关键是找出第一个来,然后借助上题结论求另外两个。 

a[]数组,假设x y z为只出现一次的数,其他出现偶数次
s^=a[i] 则x^y x^z y^z 的lowbit 这三个值有一个规律,其中肯定两个是一样的,另外一个是不一样的。令flips=上述三个值的异或。因此,可以利用此条件获得某个x(或者y,或者z),循环判断的条件是 a[i]^s的lowbit==flips
解释:a[i]^s即可划分为两组,一组是lowbit与flips不同,一组是lowbit与flips相同。这样就能找到某个x,y,z,找出后,与数组最户一个值交换,利用find2,找出剩余两个。

lowbit为某个数从右往左扫描第一次出现1的位置。其实 lowbit 函数里面写成  mark = (x&(x-1))^x  ; return mark ; 也是可以的,功能是一样的。

[cpp]   view plain copy
  1. /**   
  2. ** author :hackbuteer 
  3. ** date   :2011-10-19    
  4. **/  
  5. #include <iostream>    
  6. using namespace std;  
  7.   
  8. int lowbit(int x)  
  9. {  
  10.     return x & ~(x - 1);  
  11. }  
  12.   
  13. void Find2(int seq[], int n, int& a, int& b)  
  14. {  
  15.     int i, xors = 0;  
  16.     for(i = 0; i < n; i++)  
  17.         xors ^= seq[i];  
  18.   
  19.     int diff = lowbit(xors);  
  20.   
  21.     a = 0,b = 0;  
  22.     for(i = 0; i < n; i++)  
  23.     {  
  24.         if( diff&seq[i] )    //与运算,表示数组中与异或结果位为1的位数相同  
  25.             a ^= seq[i];  
  26.         else  
  27.             b ^= seq[i];  
  28.     }  
  29. }  
  30.   
  31. //三个数两两的异或后lowbit有两个相同,一个不同,可以分为两组  
  32. void Find3(int seq[], int n, int& a, int& b, int& c)  
  33. {  
  34.     int i, xors = 0;  
  35.     for(i = 0; i < n; i++)  
  36.         xors ^= seq[i];  
  37.   
  38.     int flips = 0;  
  39.     for(i = 0; i < n; i++)            //因为出现偶数次的seq[i]和xors的异或,异或结果不改变  
  40.         flips ^= lowbit(xors ^ seq[i]);  
  41.     //表示的是:flips=lowbit(a^b)^lowbit(a^c)^lowbit(b^c)  
  42.     //flips = lowbit(flips);    这个是多余的  
  43.   
  44.     //三个数两两异或后lowbit有两个相同,一个不同,可以分为两组  
  45.     //所以flips的值为:lowbit(a^b) 或 lowbit(a^c) 或 lowbit(b^c)  
  46.   
  47.   
  48.     //得到三个数中的一个  
  49.     a = 0;  
  50.     for(i = 0; i < n; i++)  
  51.     {  
  52.         if(lowbit(seq[i] ^ xors) == flips)     //找出三个数两两异或后的lowbit与另外两个lowbit不同的那个数  
  53.             a ^= seq[i];  
  54.     }  
  55.   
  56.     //找出后,与数组中最后一个值交换,利用Find2,找出剩余的两个  
  57.     for(i = 0; i < n; i++)  
  58.     {  
  59.         if(a == seq[i])  
  60.         {  
  61.             int temp = seq[i];  
  62.             seq[i] = seq[n - 1];  
  63.             seq[n - 1] = temp;  
  64.         }  
  65.     }  
  66.   
  67.     //利用Find2,找出剩余的两个  
  68.     Find2(seq, n - 1, b, c);  
  69. }  
  70. //假设数组中只有2、3、5三个数,2与3异或后为001,2与5异或后为111,3与5异或后为110,  
  71. //则flips的值为lowbit(110)= 2 ,当异或结果xors与2异或的时候,得到的就是3与5异或的结果110,其lowbit值等于flips,所以最先找出来的是三个数中的第一个数:2  
  72.   
  73. int main(void)  
  74. {  
  75.     int seq[]={ 2,3,3,2,4,6,4,10,9,8,8 };  
  76.     int a,b,c;  
  77.     Find3(seq, 11, a, b, c);  
  78.     cout<<a<<endl;  
  79.     cout<<b<<endl;  
  80.     cout<<c<<endl;  
  81.     return 0;  
  82. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值