问题描述:
一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。请找出这两个数 例如:1 2 3 4 5 6 1 2 3 4? -> 5 6
解题思路:主要用异或的思想
预备知识:
(1)两个相同的数异或的结果是0; (2)所有不为0的数和0异或的结果是本身。
有了以上的了解,可以将数组的元素分成两组,按照这样的原则:
比如:1 1 2 2 3 3 6, 4 4 5 分成这样的两个组之后,每个组的元素异或的结果便是要找的两个元素。
那么,如何进行分组呢?下面为具体步骤: 为了方便理解,写出1-6的二进制序列:
1 -> 001 | 2 -> 010 | 3 -> 011 | 4 -> 100 | 5 -> 101 | 6 -> 110 |
- 第一步:将所有元素异或,得到即为5异或6的结果ret,也就是ret = 011
- 第二步:ret二进制序列中为1的位,则是5和6在该位上不一样,那么可以按照这个位来分组(比如011的第1个二进制位为1,5(101)和6(110)的第一位确实不相同)
- 第三步:分组,二进制位第1位为0的数有:2(010),4(100),6(110);二进制位第1位为1的数有:1(001),3(011),5(101)
代码实现:
void Find(int arr[], int sz, int *px, int* py)
{
int i = 0;int ret = 0;int pos; int num1,num2;
//1. 要把所有数字异或
for (i = 0; i < sz; i++){
ret ^= arr[i];
}
//2. 计算ret的哪一位为1
pos = 0;
for (i = 0; i < 32; i++){
if (((ret >> i) & 1) == 1){
pos = i;
break;}
}
//3. 把从低位向高的第pos位为1的数放在一个组,为0的放在另外一个组。
num1 = 0; num2 = 0;
for (i = 0; i < sz; i++){
if (((arr[i] >> pos) & 1) == 1)
num1 ^= arr[i];
else
num2 ^= arr[i];
}
*px = num1;*py = num2;
}
int main()
{
int arr[] = { 1,2,3,4,5,6,1,2,3,4 };
int sz = sizeof(arr) / sizeof(arr[0]);
int x = 0, y = 0;//输出型参数
Find(arr, sz, &x, &y);
printf("%d %d\n", x, y);
return 0;
}
|