這一系列有三道題,第一題也是最簡單最經典的。數組
有一個數組,裏面的元素每一個都出現了兩次,除了一個特殊的,求這個特殊元素。接觸過這類題目的coder很快可以脫口而出:直接異或就ok了!的確如此:code
var singleNumber = function(nums) { return nums.reduce(function(pre, item) { return pre ^ item; }); };
可是爲什麼這樣能獲得答案?咱們假設有個數組[1, 2, 3, 1, 2]
,很顯然咱們要找出3這個元素。咱們首先將數組元素所有用二進制表示:leetcode
0 1 1 0 1 1 0 1 1 0
咱們從右往左,按位分析。若是數組中全部元素所有都是出現兩次,那麼每一位上的1的數量之和確定是2的倍數。咱們看從右往左的第一位,1出現了三次,這多出的一次就是3的起做用,從右往左數第二位,1一樣出現了三次,一樣是由於3的緣由。因此咱們能夠獲得該元素的二進制表示爲11
,也就是3。get
繼續思考,咱們彷佛須要這麼一種「加法」運算,使得每位上的 1 bit 數量可以獲得累積,可是累積到2了就能清零。幸運的是,^
運算符就是咱們要找的。it
因而代碼也就很好理解了。io
這是上題的增強版,數組中每一個元素都出現三次,除了一個特殊的,找出這個特殊元素。function
咱們以數組[1, 2, 3, 1, 2, 1, 2]
舉例,將數組元素用二進制表示:變量
0 1 1 0 1 1 0 1 1 0 0 1 1 0
若是理解了上題,此題的思路彷佛也就呼之欲出了。咱們也能夠按位運算,計算1 bit的數量,若是每一個數字都出現三次,那麼每位上的1 bit數量確定是3的倍數,相反若是不是3的倍數,那麼就是那個特殊的數在搗蛋。二進制
咱們彷佛須要這麼一種「加法」運算,使得每位上的 1 bit 數量可以獲得累計,而且累計到了3就自動清零。可是理想是美好的,現實是殘酷的,並無這樣一種神奇的運算(三進制?)。統計
可是咱們能夠用一個數「輔助」,由於每一位的1 bit數量統計都是相似的,因此假設正在統計某一位的1 bit數量。咱們用a
來表示 1 bit 的數量,當 1 bit 的數量爲0時,a=0;當數量爲1時,a=1;當數量爲2時,a=2?非也,位運算只能表示0和1,因此這時咱們引進第二個變量b,咱們用b=1來表明已經有了2個 1 bit,因此當有兩個 1 bit 時,a=0,b=1。數量統計結果逢3化0,因此只有0、一、2三種結果:
bits數量 a b 0 0 0 1 1 0 2 0 1
思路也就顯而易見了,每次運算咱們維護a和b的值,運算到最後便可獲得結果:
var singleNumber = function(nums) { var a = 0, b = 0; nums.forEach(function(item) { b = a & (b ^ item); a = b | (a ^ item); }); return a; };
固然最樸素的作法是按位枚舉每一位的 1 bit 的數量。
仍是一個數組,每一個元素出現兩次,只有兩個特殊的元素出現一次,把這兩個特殊的元素找出來。
兩個特殊的元素?這時候直接異或也並無什麼卵用了啊...以數組[1, 1, 2, 2, 3, 3, 4, 5]
舉例,如何把4和5找出來?一個數組中有兩個特殊的數字,不能用異或運算獲得結果,若是隻有一個了呢?沒錯,咱們能夠把4和5根據某一規則分到兩個數組中,而後在各自數組中進行異或從而獲得結果。
那麼如何分配?咱們把4和5用二進制表示出來看看:
1 0 0 1 0 1
由於兩個數字不相同,因此它們的二進制碼確定有一位是不一樣的。咱們只需找出這一位,而後根據這一位上是0是1,將數組全部元素分到兩個新的數組中,這時4和5確定已經被分到了不一樣的數組中,而其他兩個相同的數字確定在一個數組中,這時就能分別對兩個數組進行異或運算了。
關於這一位,能夠找右數第一個不一樣位,把兩數異或找出右邊第一個1便可,而兩數的異或其實就是原數組全部元素的異或。
var singleNumber = function(nums) { var tmp = nums.reduce(function(pre, item) { return pre ^ item; }); var one = tmp & (-tmp); // 取右數第一位1 var a = [], b = []; nums.forEach(function(item) { item & one ? a.push(item) : b.push(item); }); return [ a.reduce(function(pre, item) { return pre ^ item; }), b.reduce(function(pre, item) { return pre ^ item; }) ]; };