描述:给你一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。
你必须设计并实现线性时间复杂度的算法且仅使用常量额外空间来解决此问题。
例如:
有数组的元素是:1,2,3,4,5,1,2,3,4,6
只有5和6只出现1次,要找出5和6.
分析:
判断数字异同我们能想到的是…
-
异或
-
二进制位相同为 0,相异为 1
-
支持交换律
a ^ a = 0 // 消去相同数 0 ^ a = a // 留下不同数 eg: 2 ^ 5 ^ 2 = 5 2 ^ 2 ^ 5 = 5
^
的规则:
通过这样的规则,我们可以将一行数组里面的 1 个不同 给找出来。只需要把数组所有的元素 异或在一起。
可是这道题,不同有两个,怎么办?
也很好想到解决思路,就是 分组!!分成两组,每组各 1 个不同
关键思路是如何有效的分组,能保证两组数里,别的数都有重复,只 各有 1 个不同呢?
既然使用了二进制,就用到底吧~~
关键思路:
- 把数组所有数异或在一起,得到两个不同数的抑或结果,这里作 num1^num2
- 计算 num1^num2 的哪一位为 1,作第 n 位
- 以第 n 位为标准,第 n 位为 1 的放一组,为 0 的放一组
- 分组完成,两组各自异或在一起,就可以得出结果
代码🐎:
int main()
{
int arr[] = { 1,2,3,4,5,1,2,3,4,6 };
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
int ret = 0;
for (i = 0; i < sz; i++)
{
ret ^= arr[i];
}
// 运行到这里,ret 就是 5^6 的结果,这里面肯定有 1
// 计算 ret 的第几位为 1
int pos = 0;
for (i = 0; i < 32; i++)
{
if (((ret >> i) & 1) == 1)
{
pos = i;
}
}
// 此时 ret 的第 pos 位是 1
// 把 arr 数组种的每个元素的第 pos 位为 1 的数异或到一起
int num1 = 0;
int num2 = 0;
for (i = 0; i < sz; i++)
{
if (((arr[i] >> pos) & 1) == 1)
{
num1 ^= arr[i];
}
else
{
num2 ^= arr[i];
}
}
printf("%d %d\n", num1, num2);
return 0;
}