1. 程式人生 > >[LeetCode] Single Number II 單獨的數字之二

[LeetCode] Single Number II 單獨的數字之二

Given an array of integers, every element appears three times except for one. Find that single one.

Note:
Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory?

這道題是之前那道 Single Number 單獨的數字 的延伸,那道題的解法就比較獨特,是利用計算機按位儲存數字的特性來做的,這道題就是除了一個單獨的數字之外,陣列中其他的數字都出現了三次,那麼還是要利用位操作 Bit Operation 來解此題。我們可以建立一個32位的數字,來統計每一位上1出現的個數,我們知道如果某一位上為1的話,那麼如果該整數出現了三次,對3去餘為0,我們把每個數的對應位都加起來對3取餘,最終剩下來的那個數就是單獨的數字。程式碼如下:

解法一:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int res = 0;
        for (int i = 0; i < 32; ++i) {
            int sum = 0;
            for (int j = 0; j < nums.size(); ++j) {
                sum += (nums[j] >> i) & 1;
            }
            res 
|= (sum % 3) << i; } return res; } };

還有一種解法,思路很相似,用3個整數來表示INT的各位的出現次數情況,one表示出現了1次,two表示出現了2次。當出現3次的時候該位清零。最後答案就是one的值。

  1. ones   代表第ith 位只出現一次的掩碼變數
  2. twos  代表第ith 位只出現兩次次的掩碼變數
  3. threes  代表第ith 位只出現三次的掩碼變數

假設現在有一個數字1,那麼我們更新one的方法就是‘亦或’這個1,則one就變成了1,而two的更新方法是用上一個狀態下的one去‘與’上數字1,然後‘或’上這個結果,這樣假如之前one是1,那麼此時two也會變成1,這make sense,因為說明是當前位遇到兩個1了;反之如果之前one是0,那麼現在two也就是0。注意更新的順序是先更新two,再更新one,不理解的話只要帶個只有一個數字1的輸入陣列看一下就不難理解了。然後我們更新three,如果此時one和two都是1了,那麼由於我們先更新的two,再更新的one,two為1,說明此時至少有兩個數字1了,而此時one為1,說明了此時已經有了三個數字1,這塊要仔細想清楚,因為one是要‘亦或’一個1的,值能為1,說明之前one為0,實際情況是,當第二個1來的時候,two先更新為1,此時one再更新為0,下面three就是0了,那麼‘與’上three的相反數1不會改變one和two的值;那麼當第三個1來的時候,two還是1,此時one就更新為1了,那麼three就更新為1了,此時就要清空one和two了,讓它們‘與’上three的相反數0即可,最終結果將會儲存在one中,參見程式碼如下:

解法二:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int one = 0, two = 0, three = 0;
        for (int i = 0; i < nums.size(); ++i) {
            two |= one & nums[i];
            one ^= nums[i];
            three = one & two;
            one &= ~three;
            two &= ~three;
        }
        return one;
    }
};

下面這種解法思路也十分巧妙,根據上面解法的思路,我們把陣列中數字的每一位累加起來對3取餘,剩下的結果就是那個單獨陣列該位上的數字,由於我們累加的過程都要對3取餘,那麼每一位上累加的過程就是0->1->2->0,換成二進位制的表示為00->01->10->00,那麼我們可以寫出對應關係:

00 (+) 1 = 01

01 (+) 1 = 10

10 (+) 1 = 00 ( mod 3)

那麼我們用ab來表示開始的狀態,對於加1操作後,得到的新狀態的ab的演算法如下:

b = b xor r & ~a;

a = a xor r & ~b;

我們這裡的ab就是上面的三種狀態00,01,10的十位和各位,剛開始的時候,a和b都是0,當此時遇到數字1的時候,b更新為1,a更新為0,就是01的狀態;再次遇到1的時候,b更新為0,a更新為1,就是10的狀態;再次遇到1的時候,b更新為0,a更新為0,就是00的狀態,相當於重置了;最後的結果儲存在b中。明白了上面的分析過程,就能寫出程式碼如下;

解法三:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int a = 0, b = 0;
        for (int i = 0; i < nums.size(); ++i) {
            b = (b ^ nums[i]) & ~a;
            a = (a ^ nums[i]) & ~b;
        }
        return b;
    }
};

類似題目:

參考資料: