数组中只出现一次的数字

一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。

数字都出现两次,则异或肯定为0。

考虑下这个题目的简化版——数组中除一个数字只出现1次外,其它数字都成对出现,要求尽快找出这个数字。根据异或运算的特点,直接异或一次就可以找出这个数字。

现在数组中有两个数字只出现1次,直接异或一次只能得到这两个数字的异或结果,但光从这个结果肯定无法得到这个两个数字。因此我们来分析下简化版中“异或”解法的关键点,这个关键点也相当明显——数组只能有一个数字出现1次

    设题目中这两个只出现1次的数字分别为A和B,如果能将A,B分开到二个数组中,那显然符合“异或”解法的关键点了。因此这个题目的关键点就是将A,B分开到二个数组中。由于A,B肯定是不相等的,因此在二进制上必定有一位是不同的。根据这一位是0还是1可以将A,B分开到A组和B组。而这个数组中其它数字要么就属于A组,要么就属于B组。再对A组和B组分别执行“异或”解法就可以得到A,B了。而要判断A,B在哪一位上不相同,只要根据A异或B的结果就可以知道了,这个结果在二进制上为1的位都说明A,B在这一位上是不相同的。

    比如int a[] = {1, 1, 3, 5, 2, 2}

    整个数组异或的结果为3^5即 0x0011 ^ 0x0101 = 0x0110

    对0x0110,第1位(由低向高,从0开始)就是1。因此整个数组根据第1位是0还是1分成两组。

    a[0] =1  0x0001  第一组

    a[1] =1  0x0001  第一组

    a[2] =3  0x0011  第二组

    a[3] =5  0x0101  第一组

    a[4] =2  0x0010  第二组

    a[5] =2  0x0010  第二组

    第一组有{1, 1, 5},第二组有{3, 2, 3},明显对这二组分别执行“异或”解法就可以得到5和3了。

 1 class Solution {
 2 public:
 3     void FindNumsAppearOnce(vector<int> data,int* num1,int *num2) {
 4         int temp=0;
 5         int n=data.size();
 6         if(n<2) return;
 7         int i,j;
 8         for(i=0;i<n;i++)
 9             temp^=data[i];
10         for(j=0;j<sizeof(int)*8;j++){
11             if((temp>>j)&1==1)
12                 break;
13         }
14         for(i=0;i<n;i++){
15             if((data[i]>>j)&1==1){
16                 *num1^=data[i];
17             }
18             else
19                 *num2^=data[i];
20         }
21     }
22 };

扩展:数组A中,除了某一个数字x之外,其他数字都出现了三次,而x出现了一次。请给出最快的方法找到x。

我们换一个角度来看,如果数组中没有x,那么数组中所有的数字都出现了3次,在二进制上,每位上1的个数肯定也能被3整除。如{1, 5, 1, 5, 1, 5}从二进制上看有:

1:0001

5:0101

1:0001

5:0101

1:0001

5:0101

二进制第0位上有6个1,第2位上有3个1.第1位和第3位上都是0个1,每一位上的统计结果都可以被3整除。而再对该数组添加任何一个数,如果这个数在二进制的某位上为1都将导致该位上1的个数不能被3整除。因此通过统计二进制上每位1的个数就可以推断出x在该位置上是0还是1了,这样就能计算出x了。

推广一下,所有其他数字出现N(N>=2)次,而一个数字出现1次都可以用这种解法来推导出这个出现1次的数字。

 1 // 【白话经典算法系列之十七】<span style="color:#000000;font: 14px/23px arial; text-transform: none; text-indent: 0px; letter-spacing: normal; word-spacing: 0px; float: none; display: inline !important; white-space: normal; font-size-adjust: none; font-stretch: normal; -webkit-text-stroke-width: 0px;">数组中只出现一次的数</span>
 2 //  by MoreWindows( http://blog.csdn.net/MoreWindows ) 
 3 //  欢迎关注http://weibo.com/morewindows
 4 #include <stdio.h>
 5 #include <string.h>
 6 int FindNumber(int a[], int n)
 7 {
 8   int bits[32];
 9   int i, j;
10   // 累加数组中所有数字的二进制位
11   memset(bits, 0, 32 * sizeof(int));
12   for (i = 0; i < n; i++)
13     for (j = 0; j < 32; j++)
14       bits[j] += ((a[i] >> j) & 1);
15   // 如果某位上的结果不能被整除,则肯定目标数字在这一位上为
16   int result = 0;
17   for (j = 0; j < 32; j++)
18     if (bits[j] % 3 != 0)
19       result += (1 << j);
20   return result;
21 }
22 int main()
23 {
24   printf("    【白话经典算法系列之十七】数组中只出现一次的数\n");
25   printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n");
26   printf(" -- http://blog.csdn.net/morewindows/article/details/12684497 -- \n\n");
27 
28   const int MAXN = 10;
29   int a[MAXN] = {2, 3, 1, 2, 3, 4, 1, 2, 3, 1};
30   printf("%d\n", FindNumber(a, MAXN));
31   return 0;
32 }

运行结果如下图所示:

 

 

本文地址:http://blog.csdn.net/morewindows/article/details/12684497

posted @ 2015-09-01 23:33  鸭子船长  阅读(471)  评论(0编辑  收藏  举报