LeetCode 260. Single Number III 解题报告
题目描述
Given an array of numbers nums, in which exactly two elements appear only once and all the other elements appear exactly twice. Find the two elements that appear only once.
示例
Given nums = [1, 2, 1, 3, 2, 5], return [3, 5].
限制条件
- The order of the result is not important. So in the above example, [5, 3] is also correct.
- Your algorithm should run in linear runtime complexity. Could you implement it using only constant space complexity?
解题思路
我的思路:
之前做了这道题的简单版本: LeetCode 136. Single Number,里面介绍了找到唯一不重复出现的数字的算法是使用位异或运算,然而这道题中是有两个唯一的数字
A
和
使用一个set存储数组中的元素,当元素第一次出现时就放入到set中,当元素第二次出现时就从set中删除该元素,最后set中仅剩下只出现一次的元素,返回这些元素即可。
这种算法是可以通过的,但不是最好的解法。下面是使用位操作运算的最优解法。
参考思路:
之前的博文里讲过异或运算具有以下性质(这里用
∧
表示异或操作):
0∧N=N
N∧N=0
所以有以下等式成立:
0∧N1∧N2∧N3∧N2∧N3
=0∧(N2∧N2)∧(N3∧N3)∧N1
=0∧(0)∧(0)∧N1
=N1
因此通过将数组里的所有数字都异或一次,我们能得到
A∧B
。
问题在于我们不知道
A
或
由于A跟B是不一样的数,所以
A∧B≠0
,因此
A∧B
的二进制表示中至少存在一个bit是1。根据异或的定义,这个bit是1,表明了在该位置上,
A
和
我们可以使用这个bit作为一个标志位,对原来的数组的元素进行划分。假设是第i个bit为1,那么我们设置一个变量bitFlag,bitFlag仅在第i个bit为1,其它bit都为0,用bitFlag与数组中的元素相与,相与结果为0的归为第1组,相与结果不为0的归为第2组,这样
A
和
这里还有一个关键点,那就是bitFlag的设置。当然,你可以用原始的方式,通过移位找到 A∧B 的某一个为1的bit的位置,然后构建这个bitFlag变量。然而,存在一种很简单的方式设置bitFlag的值,这里选择的是 A∧B 最右边值为1的bit作为标准位:
int bitFlag = xorSum & (~(xorSum - 1));
乍看之下,这种方式这奇怪,也不直观,所以我详细讲解一下它的原理:
1.在LeetCode 191.Number of 1 Bits一文中我们知道n -1会把n的最低的为1的bit变成0,剩下的bit为1,把这个bit称为第i个bit。如下图
2.当对n-1取反后,第i个bit左边的位都变成相反的值,而第i个bit变回1,第i个bit右边的位都变回0。
如果以上图的n为例,即~(n -1):…001 1 00
3.对n与~(n -1)进行相与,得到的就是仅在第i个位为1,其它位为0的值,因为n和(n-1)的第i个bit左边的所有位都不相同(第二步对n-1取反的结果),第i个bit都为1,第i个bit右边的位均为0,所以n&(~(n -1))能得到我们想要的结果。
代码
我的代码
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
set<int> single;
set<int>::iterator itr = single.end();
vector<int> result;
for (int i = 0; i < nums.size(); i++) {
itr = single.find(nums[i]);
if (itr == single.end()) {
single.insert(nums[i]);
} else {
single.erase(nums[i]);
}
}
for (itr = single.begin(); itr != single.end(); itr++) {
result.push_back(*itr);
}
return result;
}
};
参考代码
class Solution {
public:
vector<int> singleNumber(vector<int>& nums) {
vector<int> single(2, 0);
int xorSum = 0;
// Calculate A xor B
for (int i = 0; i < nums.size(); i++) {
xorSum ^= nums[i];
}
// Find the lowest bit that A different from B
// set that bit to 1 while other bits are 0
int bitFlag = xorSum & (~(xorSum - 1));
for (int i = 0; i < nums.size(); i++) {
// Numbers that have 0 at that bit
if ((bitFlag & nums[i]) == 0) {
single[0] ^= nums[i];
}
// Numbers that have 1 at that bit
else {
single[1] ^= nums[i];
}
}
return single;
}
};
总结
真心感觉做题时方向要对,虽然我知道会用位异或操作,但是我却对着
A∧B
苦思怎么分解出
A
和
终于填了今天的坑,明天加油!