题目链接
题目描述
给你一个整数数组 n u m s nums 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 ≤ n u m s . l e n g t h ≤ 3 ∗ 1 0 4 2 \leq nums.length \leq 3 * 10^4 2≤nums.length≤3∗104
- - 2 31 ≤ n u m s [ i ] ≤ 2 31 − 1 2^{31} \leq nums[i] \leq 2^{31} - 1 231≤nums[i]≤231−1
- 除两个只出现一次的整数外, n u m s nums nums 中的其他数字都出现两次
解法:位运算
由于 n u m s nums nums 中只有两个数 x x x 和 y y y 分别出现一次,其他元素都出现了两次。
所以直接对整个 n u m s nums nums 进行 异或,最后的结果实际上就是 x x o r y x \quad xor \quad y xxory,因为相同的元素异或为0。
假设此时的 x = 3 = ( 0011 ) 2 , y = 5 = ( 0101 ) 2 x = 3 = (0011)_2 , y = 5 = (0101)_2 x=3=(0011)2,y=5=(0101)2 , 异或的结果为 t = 6 = ( 0110 ) 2 t = 6 = (0110)_2 t=6=(0110)2。
假设 t t t 的第 k k k 二进制位为 1 1 1 得话,说明要么是 x x x 第 k k k 个二进制位为 1 1 1;说明要么是 y y y 第 k k k 个二进制位为 1 1 1。不可能同时为 1 1 1。
所以通过这个 ,我们就可以将 x x x 和 y y y 分开。因为直接将 x x x 和 y y y 对 2 k − 1 2^{k - 1} 2k−1 分别进行 与操作,结果分别为 2 k − 1 2^{k - 1} 2k−1 和 0 0 0。
另外,由于其他元素都是成对出现的,所以每一对元素对 2 k − 1 2^{k - 1} 2k−1 进行 与操作,结果都是一样的,也就是他们会被分到同一组。
假设结果 t = ( 101011100010 ) 2 t = (101011100010)_2 t=(101011100010)2,使用任何一个 1 1 1 来进行上述的操作其实都可以的,想用哪个就用哪个。不过为了方便起见,直接使用最低位的那个 1 1 1 ,也就是 t = ( 101011100010 ) 2 t = (1010111000 \mathbf{1}0)_2 t=(101011100010)2
我们可以通过以下操作得到 t t t 最低位的 1 1 1:
int k = t & (-t);
t = 1010 1110 0010
&
-t = 0101 0001 1110
0000 0000 0001
时间复杂度: O ( n ) O(n) O(n)
C++代码:
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
unsigned int t = 0;
for(auto x:nums) t ^= x;
int k = t & ( -t);
vector<int> ans(2);
for(auto x:nums){
if((x & k) == k) ans[0] ^= x;
else ans[1] ^= x;
}
return ans;
}
};