http://blog.chinaunix.net/uid-26448049-id-3024001.html
1: 任何一个数与自己异或结果为0
2:任何一个数与0异或结果为自己
异或无关先后顺序。
题目:一个整型数组里除了两个数字之外,其他的数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。
来自: http://www.cnblogs.com/mingzi/archive/2009/08/04/1538470.html http://blog.csdn.net/zhuxiaoyang2000/article/details/6259233
分析:首先我们考虑这个问题的一个简单版本:一个数组里除了一个数字之外,其他的数字都出现了两次。请写程序找出这个只出现一次的数字。
这个题目的突破口在哪里?题目为什么要强调有一个数字出现一次,其他的出现两次?我们想到了异或运算的性质:任何一个数字异或它自己都等于0。也就是说,如果我们从头到尾依次异或数组中的每一个数字,那么最终的结果刚好是那个只出现一次的数字,因为那些出现两次的数字全部在异或中抵消掉了。
有了上面简单问题的答案之后,我们回到原始的问题。如果能够把原数组分为两个子数组,在每个子数组中,包含一个只出现一次的数字,而其他数字都出现两次,那么按照前面的办法就可以分别求出这两个只出现一次的数字了。
假如这两个数为a和b,那么将所有的数异或得到的数必定为a^b。由于a和b不相等,那么a^b != 0,也就是说在a^b中必定至少有一位为1,对应位上a与b不一样,根据这一位,我们可以将a和b分开,并将数分成两组。注意,两个相同的数肯定会被分到同一个组。 我们在结果数字中找到第一个为1的位的位置,记为第N位。现在我们以第N位是不是1为标准把原数组中的数字分成两个子数组,第一个子数组中每个数字的第N位都为1,而第二个子数组的每个数字的第N位都为0。
现在我们已经把原数组分成了两个子数组,每个子数组都包含一个只出现一次的数字,而其他数字都出现了两次。因此到此为止,所有的问题我们都已经解决。
- int findNotDouble(int a[], int n)
- {
- int result = a[0];
- int i;
- for(i = 1; i < n; ++i)
- result ^= a[i];
- return result;
- }
-
- void findOutTwoOdds(int a[], int n, int &odd1, int &odd2)
- {
- int x = findNotDouble(a, n);
- int y=1, i;
- // find 'y' to indicate the first bit which is 1 in (a^b)
- for(y = 1; y <= x; y <<= 1)
- {
- if(y & x)
- break;
- }
- // divide the datas into two groups
- for(i = 0; i < n; ++i)
- {
- if(y & a[i])
- odd1 ^= a[i];
- else
- odd2 ^= a[i];
- }
- }
- int main()
- {
- int A[]={4, 5, 3, 4, 5, 7, 7, 6, 8, 8};
- int num = sizeof(A)/sizeof(int);
- int odd1 = 0, odd2 = 0;
-
- findOutTwoOdds(A, num, odd1,odd2);
- printf("the two numbers are %d and %d.\n", odd1, odd2);
- return 0;
- }