位运算 - 只出现一次的数字 III - Leetcode 260
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案。
进阶:你的算法应该具有线性时间复杂度。你能否仅使用常数空间复杂度来实现?
示例 1:
输入:nums = [1,2,1,3,2,5]
输出:[3,5]
解释:[5, 3] 也是有效的答案。
示例 2:
输入:nums = [-1,0]
输出:[-1,0]
示例 3:
输入:nums = [0,1]
输出:[1,0]
提示:
2 ≤ nums.length ≤ 3 * 104
-231 ≤ nums[i] ≤ 231 - 1
除两个只出现一次的整数外,nums 中的其他数字都出现两次
分析:
若只有一个数出现了一次,那么直接求异或和即可。
对于有两个数(假设是 n1
、n2
)出现一次的情况,我们求异或和,最终会得到这两个数异或的值 n1^n2 = xor_sum
,我们考虑分析这个值。
可以确定,xor_sum
≠ 0,否则说明 n1
= n2
,就与题意矛盾。
那我们就考虑 xor_sum
的位表示,假设其第
k
k
k 位为
1
1
1,那么可以肯定,n1
和 n2
的第
k
k
k 位不同,即一个是
1
1
1,另一个是
0
0
0。
那么我们就可以根据第 k k k 位的值,把这一堆数分成两组:第 k k k 位为 1 1 1 的一组、第 k k k 位为 0 0 0 的一组。
然后,我们对这两组分别求异或和。可以肯定的是,出现两次的数一定会被分到同一组,那么这就不会影响最后的异或和。
所以,最后这两组的异或和就是只出现一次的两个数。
代码:
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
int xor_sum = 0;
for(int i = 0; i < nums.size(); i ++)
xor_sum ^= nums[i];
// 官方题解用了与上相反数的方法,求得最低位的 1,更简洁。但实际上本题找到任意位置的 1 均可。
int k = 0;
for(int i = 31; i >= 0; i --)
if(xor_sum >> i & 1)
{
k = i;
break;
}
int n1 = 0, n2 = 0;
for(int i = 0; i < nums.size(); i ++)
{
int num = nums[i];
if(num >> k & 1) n1 ^= num;
else n2 ^= num;
}
return {n1, n2};
}
};