一、问题概述
-
一个数组中只有两个数字是出现一次,其他所有数字都出现了两次。
-
编写一个函数找出这两个只出现一次的数字。
-
例如:
-
有数组的元素是:1,2,3,4,5,1,2,3,4,6
-
只有5和6只出现1次,要找出5和6.
二、异或运算的性质
^是c语言中的一种对二进制直接操作的操作符,两个数对应的二进制位相同,异或结果为0,不同,异或结果为1。
-
例如:2 ^ 3
-
2的二进制位:
-
000000000 00000000 00000000 00000010
-
3的二进制位:
-
000000000 00000000 00000000 00000011
-
2 ^ 3:
-
000000000 00000000 00000000 00000001
三、异或运算的规律
-
任何数与0进行异或运算,结果为这个数本身;
- 2 ^ 0 = 2
-
任何数与自身进行异或运算,结果为0;
- 2 ^ 2 = 0
-
异或运算满足交换律和结合律。
- 2 ^ 3 = 3 ^ 2
- 2 ^ 3 ^ 2 = 2 ^ 2 ^ 3
四、解决思路
-
遍历数组,依次对数组中的所有数字进行异或运算,最终得到的结果是两个只出现一次的数字异或的结果。
-
在这个结果中,找到为1的位数,将数组中的数字按照该位是否为1分成两组。
-
分别对这两组数字进行异或运算,得到的结果就是两个只出现一次的数字。
五、代码实现
- 以下是C语言的实现代码:
#include <stdio.h>
void Find_Two_Numbers(int arr[], int n, int *num1, int *num2)
{
int result = 0;
int i = 0;
int bit = 0;
// 对数组中的所有数字进行异或运算
for (i = 0; i < n; i++)
{
result ^= arr[i];
}
// 找到异或结果中为1的位数
// 一个整型比特位的位数因计算机类型而异,有32位的,也有64位的
for (bit = 0; bit < sizeof(int) * 8; bit++)
{
if ((result >> bit) & 1)
{
break;
}
}
// 赋值为0是因为任何数异或0等于它本身
*num1 = 0;
*num2 = 0;
// 因为同0异1,所以在这一位上,两个数一定是一个1一个0
// 根据该位是否为1分成两组进行异或运算
for (i = 0; i < n; i++)
{
if ((arr[i] >> bit) & 1)
{ // 该位为1的数字
*num1 ^= arr[i];
}
else
{ // 该位为0的数字
*num2 ^= arr[i];
}
}
}
int main()
{
int arr[] = {1,2,3,4,5,1,2,3,4,6};
int num1 = 1;
int num2 = 1;
int n = sizeof(arr) / sizeof(arr[0]);
Find_Two_Numbers(arr, n, &num1, &num2);
printf("Two numbers that appear only once: %d, %d\n", num1, num2);
return 0;
}
运行结果:
Two numbers that appear only once: 5, 6