天天看點

劍指Offer56-Ⅱ.數組中數字出現的次數Ⅱ

  • 題目:劍指Offer56-Ⅱ.數組中數字出現的次數Ⅱ

    輸入一個數組nums,其中隻有一個數出現一次,其餘數均出現3次;

    找到那個出現一次的數;

    1 <= nums.length <= 10000;(說明n ^ 2也可以)

    1 <= nums[i] < 2^31

  • 思路:1,2正常思路,3,4好辦法

    1.哈希表:時間O(n):掃兩次nums,空間O(n):幾乎每個數都出現3次,是以哈希表隻需要存n/3的數即可;

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        unordered_map<int, int> count;
        for (int x : nums) ++count[x];//第一次周遊存下每個數出現的次數

        for (int x : nums) 
            if (count[x] == 1) return x;//第二次周遊,找到那個隻出現一次的數

        return -1;//如果一定有隻出現一次的數的話,是走不到這裡的
    }
};
           

3.sort:時間O(nlogn),空間O(logn)

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int n = nums.size();
        sort(nums.begin(), nums.end());
        for (int i = 0; i < n - 1; ) {
            if (nums[i] == nums[i + 1]) i += 3;//跳過這3個相同數字
            else return nums[i];//否則說明找到了
        }
        
        return nums[n - 1];//走到這說明目标數字在結尾,被跳過了
    }
};
           

3.逐位周遊:時間O(n),空間O(1):額外需要一個大小為32的數組;

例如3,3,3,4,其中3出現3次,4出現1次

0 1 1 = 3
0 1 1 = 3
0 1 1 = 3
1 0 0 = 4
-----
1 3 3
%3
1 0 0 = 4
           
class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int count[32] = {0};//個位,十位,百位。。。,int共32位
        for (int x : nums) {//第一次掃,統計這些數字每位為1的個數;根據局部性原理,每個數統計完再下個數,比先統計每個數的第0位再統計每個數的第1位要好
            //盡管這裡m一定是>1的,左移是邏輯左移,但lc認為有符号數的左移是未定義的,是以這裡需要定義為unsigned
            for (int i = 0, unsigned int m = 1; i < 32; ++i, m <<= 1) {
                if (x & m) count[i]++;//這種寫法x不動,不斷右移m,去&x
            }
            /*
            for (int i = 0; i < 32; ++i) {
            	count[i] += (x & 1);
            	x >> 1;//這種寫法不斷>>,去&1更直覺,但要注意,為了防止改變數組中的x,外層for循環不能寫for(int& x : nums)
			}
			*/
        }
    
        int res = 0;
        for (int i = 31; i >= 0; --i) {//第二次掃,對每位%3,無論出現次數為3的數字在該為是0還是1,%3就把它們都去掉了,隻剩下出現次數為1的目标數的該位
            res  = (res << 1) + (count[i] % 3);//類似于十進制,從高位往低位讀,例如234,0*10+2=2,2*10+3=23,23*10+4=234
        } 
        
        return res;
    }
};
           

4.(最優解)有限狀态自動機:

人都傻了,看不懂

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int one = 0;
        int two = 0;
        for(auto n:nums){
            one = (one^n) & ~two;
            two = (two^n) & ~one;
        }
        return one;
    }
};
           
  • 總結:

    有限狀态機不會用啊!!

繼續閱讀