寫作來源
今天在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 ,因爲只有兩個位的值不一樣才能夠異或出
1 1 。這就說明
diff d i f f 的值是可以作爲標識符的。
其實這裏我們可以不用管上面所說的
c0,c1⋯cm c 0 , c 1 ⋯ c m 和
d0,d1⋯dk d 0 , d 1 ⋯ d k 分別是多少,反正也會被這個標識符分成兩個集合,我們只需要關心能被分成兩個集合即可。