一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。
数字都出现两次,则异或肯定为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- 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(" 【白话经典算法系列之十七】数组中只出现一次的数 "); 25 printf(" -- by MoreWindows( http://blog.csdn.net/MoreWindows ) -- "); 26 printf(" -- http://blog.csdn.net/morewindows/article/details/12684497 -- "); 27 28 const int MAXN = 10; 29 int a[MAXN] = {2, 3, 1, 2, 3, 4, 1, 2, 3, 1}; 30 printf("%d ", FindNumber(a, MAXN)); 31 return 0; 32 }
运行结果如下图所示:
本文地址:http://blog.csdn.net/morewindows/article/details/12684497