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.
Example:
Input:[1,2,1,3,2,5]
Output:[3,5]
Note:
- 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?
這道題是之前那兩道 Single Number 和 Single Number II 的再次延伸,說實話,這類位操作 Bit Manipulation 的題,如果之前沒有遇到過類似的題目,楞想是很難相出來的,於是我只能上網搜大神們的解法,發現還真是巧妙啊。這道題其實是很巧妙的利用了 Single Number 的解法,因為那道解法是可以准確的找出只出現了一次的數字,但前提是其他數字必須出現兩次才行。而這題有兩個數字都只出現了一次,那么我們如果能想辦法把原數組分為兩個小數組,不相同的兩個數字分別在兩個小數組中,這樣分別調用 Single Number 的解法就可以得到答案。那么如何實現呢,首先我們先把原數組全部異或起來,那么我們會得到一個數字,這個數字是兩個不相同的數字異或的結果,我們取出其中任意一位為 ‘1’ 的位,為了方便起見,我們用 a &= -a 來取出最右端為 ‘1’ 的位,具體來說下這個是如何操作的吧。就拿題目中的例子來說,如果我們將其全部 '異或' 起來,我們知道相同的兩個數 '異或' 的話為0,那么兩個1,兩個2,都抵消了,就剩3和5 '異或' 起來,那么就是二進制的 11 和 101 '異或' ,得到110。然后我們進行 a &= -a 操作。首先變負數吧,在二進制中負數采用補碼的形式,而補碼就是反碼 +1,那么 110 的反碼是 11...1001,那么加1后是 11...1010,然后和 110 相與,得到了 10,就是代碼中的 diff 變量。得到了這個 diff,就可以將原數組分為兩個數組了。為啥呢,我們想阿,如果兩個相同的數字 '異或' ,每位都會是0,而不同的數字 '異或' ,一定會有對應位不同,一個0一個1,這樣 '異或' 是1。比如3和5的二進制 11 和 101,如果從低往高看,最開始產生不同的就是第二位,那么我們用第二位來和數組中每個數字相與,根據結果的不同,一定可以把3和5區分開來,而其他的數字由於是成對出現,所以區分開來也是成對的,最終都會 '異或' 成0,不會3和5產生影響。分別將兩個小組中的數字都異或起來,就可以得到最終結果了,參見代碼如下:
class Solution { public: vector<int> singleNumber(vector<int>& nums) { int diff = accumulate(nums.begin(), nums.end(), 0, bit_xor<int>()); diff &= -diff; vector<int> res(2, 0); for (auto &a : nums) { if (a & diff) res[0] ^= a; else res[1] ^= a; } return res; } };
類似題目:
參考資料:
https://leetcode.com/problems/single-number-iii/