写作来源
今天在LeetCode上面连续做了Single Number 2和Single Number 3,非常羞耻地说,两题都是看了别人的思路理解以后再写出来的,Single Number 2的解法有人已经在简书上面写了,我这里就解释一些LeetCode上面的Discuss部分的其他人的解法。
思路
首先贴上别人写的代码。
class Solution
{
public:
vector<int> singleNumber(vector<int>& nums)
{
// Pass 1 :
// Get the XOR of the two numbers we need to find
int diff = accumulate(nums.begin(), nums.end(), 0, bit_xor<int>());
// Get its last set bit
diff &= -diff;
// Pass 2 :
vector<int> rets = {0, 0}; // this vector stores the two numbers we will return
for (int num : nums)
{
if ((num & diff) == 0) // the bit is not set
{
rets[0] ^= num;
}
else // the bit is set
{
rets[1] ^= num;
}
}
return rets;
}
};
第一步,如果说写了Single Number的人首先会把所有的数进行异或,最终得到两个不重复两次的数字的异或的结果,这里记为:
diff=b0xorb1
d
i
f
f
=
b
0
x
o
r
b
1
此时,设所有重复了两次的数字分别为 a0,a1,a2⋯an a 0 , a 1 , a 2 ⋯ a n ,而本题的思想就是通过某一种标识把 b0 b 0 和 b1 b 1 分离,或者说是把整个输入的列表分成两个部分,这两个部分分别为:
c0,c1⋯cm,b0d0,d1⋯dk,b1{a0,a1,a2⋯an}={c0,c1⋯cm}∪{d0,d1⋯dk}(523)
(523)
c
0
,
c
1
⋯
c
m
,
b
0
d
0
,
d
1
⋯
d
k
,
b
1
{
a
0
,
a
1
,
a
2
⋯
a
n
}
=
{
c
0
,
c
1
⋯
c
m
}
∪
{
d
0
,
d
1
⋯
d
k
}
因为集合中没有重复的数字,所以这里为了表示重复数字,加上上标表示重复,即比如 a0 a 0 的两个重复值为 a10 a 0 1 和 a11 a 1 1 ,所以在上面的两个集合中,存在:
c00xorc10xorc01xorc11xor⋯xorb0=b0d00xord10xord01xord11xor⋯xorb1=b1(524)
(524)
c
0
0
x
o
r
c
0
1
x
o
r
c
1
0
x
o
r
c
1
1
x
o
r
⋯
x
o
r
b
0
=
b
0
d
0
0
x
o
r
d
0
1
x
o
r
d
1
0
x
o
r
d
1
1
x
o
r
⋯
x
o
r
b
1
=
b
1
由上面的代码可以看出来,这个标识符为 diff d i f f ,原因是,通过 diff=diffxor(−diff) d i f f = d i f f x o r ( − d i f f ) 计算以后, diff d i f f 的值只保留了原本的数的最低位,其他的位的值都是0,这算是位运算中最基本的技巧吧。也就是说,原本的 diff d i f f 的最低位也是1,从而推断 b0 b 0 和 b1 b 1 的相应位肯定分别为 0 0 和,因为只有两个位的值不一样才能够异或出 1 1 。这就说明的值是可以作为标识符的。
其实这里我们可以不用管上面所说的 c0,c1⋯cm c 0 , c 1 ⋯ c m 和 d0,d1⋯dk d 0 , d 1 ⋯ d k 分别是多少,反正也会被这个标识符分成两个集合,我们只需要关心能被分成两个集合即可。