题目:一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。
编写一个函数找出这两个只出现一次的数字
1.基本思路
考虑用异或的方法来实现,找出一个只出现过一次的数字的问题处理方法就是找一个数字把里面所有的数字都异或一遍,利用异或两次等于没异或的特点来处理。那么如果有两个数字都只出现了一次,那么如此得到的应该是两个数异或的结果。(这是因为任何数和自己的异或结果都为0,任何数与0的异或结果都为这个数本身;又因为异或运算满足交换律和结合律,故得以上结论);理解了这点,剩下的就变得easy很多。
首先这个结果肯定不是0(要不然就全都配对了),所以其二进制比特位中至少有一位是1。找出值为1的某一位,(随便找一位就行了)以这一位的值将结果分为两组。(由异或运算的定义,若某一位的异或结果为1,则说明在这一位上,两个“单身狗”的二进制是不相同的,不是1就是0;因此以此条件将待查找数组分为两组,两个“单身狗”也会同时被分到各自的组中,再进行后续的查找即可)
例如1 2 3 4 1 2,异或完的结果应该是3^4得到的111,如果找最低的一位,那么这一位是1的有1 3 1,是0的有2 4 2,由于是利用异或结果为1的某一位分的组,所以两个待查询数字一定分别在两组中。所以再分别异或两组数,结果即可找到这两个数。
2.代码实现
void Find_SiDog(int ar[], int n,int* pnum1,int* pnum2)
{
int res = 0;
int i = 0;
for (; i < n; i++)
{
res ^= ar[i];
}
int pos;
for (i = 0; i < 32; i++)//找到异或结果中为1的某一位并得到位数i,存入pos;
{
if (res & 1 << i)
{
pos = i;
break;
}
}
*pnum1 = *pnum2 = 0;
for (i = 0; i < n; i++)//对数组进行分组;
{
if (ar[i] & 1 << pos)
{
*pnum1 ^= ar[i]; //这一位是1的,放在数1里
}
else
{
*pnum2 ^= ar[i]; //这一位是0的,放在数2里
}
}
}
int main()
{
int arr[] = {4 , 6, 8, 2, 3, 1, 4, 5, 2, 6, 1, 7, 8, 5 };
int n = sizeof(arr) / sizeof(arr[0]);
int num1, num2;
Find_SiDog(arr,n,&num1,&num2);
printf("%d ", num1);
printf("%d ", num2);
printf("\n");
return 0;
}