1. 程式人生 > 其它 >劍指 Offer 56. 陣列中數字出現的次數

劍指 Offer 56. 陣列中數字出現的次數

技術標籤:刷題

劍指 Offer 56 - I. 陣列中數字出現的次數

一個整型陣列 nums 裡除兩個數字之外,其他數字都出現了兩次。請寫程式找出這兩個只出現一次的數字。要求時間複雜度是O(n),空間複雜度是O(1)。

輸入:nums = [4,1,4,6]
輸出:[1,6] 或 [6,1]
輸入:nums = [1,2,10,4,1,4,3,3]
輸出:[2,10] 或 [10,2]

如果只有一個數字出現一次,其他均出現兩次的話,很容易想到用異或^的方法來實現,但是現在有兩個數字出現了一次。

如果我們可以把所有數字分成兩組,使得:

  1. 兩個只出現一次的數字在不同的組中;

  2. 相同的數字會被分到相同的組中。

  3. 先對所有數字進行一次異或,得到兩個出現一次的數字的異或值。

  4. 在異或結果中找到任意為 1 的位。 (兩個數字某一個不同,他們的異或值才為一,所以可以根據這個把他們分成兩組)

  5. 根據這一位對所有的數字進行分組。

  6. 在每個組內進行異或操作,得到兩個數字。

劍指 Offer 56 - II. 陣列中數字出現的次數 II

在一個數組 nums 中除一個數字只出現一次之外,其他數字都出現了三次。請找出那個只出現一次的數字

輸入:nums = [3,4,3,3]
輸出:4
輸入:nums = [9,1,7,9,7,9,7]
輸出:1

純位運算

如果使用異或運算,當出現三次的時候,其中的兩次就會相互抵消,所以不能使用異或的方法。

儘管我們這裡不能應用異或運算,我們還是可以沿用位運算的思路。

如果一個數字出現三次,那麼它的二進位制表示的每一位(0或者1)也出現三次。如果把所有出現三次的數字的二進位制表示的每一位都分別加起來,那麼每一位的和都能被3整除。如果某一位的和能被3整除,那麼那個只出現一次的數字二進位制表示中對應的那一位是0;否則就是1;

上述思路同樣適用於陣列中一個數字出現一次,其他數字出現奇數次問題(如果是偶數次,直接用異或就可)。

這種解法的時間效率是O(n)。我們需要一個長度為32的輔助陣列儲存二進位制表示的每一位的和。由於陣列的長度是固定的,因此空間效率是O(1)。

class Solution {
    public
int singleNumber(int[] nums) {//本演算法同樣適用於陣列nums中存在負數的情況 if (nums.length == 0) return -1;//輸入陣列長度不符合要求,返回-1; int[] bitSum = new int[32];//java int型別有32位,其中首位為符號位 int res = 0; for (int num : nums) { int bitMask = 1;//需要在這裡初始化,不能和res一起初始化 for (int i = 31; i >= 0; i--) {//bitSum[0]為符號位 //這裡同樣可以通過num的無符號右移>>>來實現,否則帶符號右移(>>)左側會補符號位,對於負數會出錯。 //但是不推薦這樣做,最好不要修改原陣列nums的資料 if ((num & bitMask) != 0) bitSum[i]++;//這裡判斷條件也可以寫為(num&bitMask)==bitMask,而不是==1 bitMask = bitMask << 1;//左移沒有無符號、帶符號的區別,都是在右側補0 } } for (int i = 0; i < 32; i++) {//這種做法使得本演算法同樣適用於負數的情況 res = res << 1; res ^= bitSum[i] % 3;//這兩步順序不能變,否則最後一步會多左移一次 } return res; } }

有窮自動機+位運算

智商真的被碾壓啊。。。這些人太強了。。

只出現一次的數字 II - 只出現一次的數字 II - 力扣(LeetCode) (leetcode-cn.com)

那是真滴牛皮

class Solution {
    public int singleNumber(int[] nums) {
        int ones = 0, twos = 0;
        for (int x: nums) {
            // 之前出現過兩次的,這次再出現就是出現了三次
            int threes = twos & x;

            // 之前出現過兩次,這次沒出現,是出現了兩次。
            // 之前出現過一次的,這次再出現,也是出現了兩次。
            twos = (twos & ~x) | (ones & x);

            // 統計記錄出現了奇數次的,並從其中清除出現三次的。
            // 這樣ones裡面始終只會記錄出現了一次的。
            ones = ones ^ x;
            ones &= ~threes;
        }
        return ones;
    }
}