leetcode:只出現一次的數

136:只出現一次的數

題目:
給定一個非空整數數組,除了某個元素只出現一次以外,其餘每個元素均出現兩次。找出那個只出現了一次的元素。
求解方法:

  1. map平衡二叉樹。統計每個元素出現的次數,返回次數爲1的元素即可。時間複雜度爲o(nlogn)o(nlogn),空間複雜度爲o(n)o(n).
  2. unordered_map散列表。統計每個元素出現的次數,返回次數爲1的元素即可。時間複雜度爲o(nlogn)o(nlogn),空間複雜度爲o(n)o(n).
  3. 異或位運算。將數組中所有元素進行異或運算,返回最後異或的結果即可。時間複雜度爲o(n)o(n),空間複雜度爲o(1)o(1).

注意事項:

  1. 利用異或的原因:

0a=aaa=0:ab=ba:(ab)c=a(bc) 0 異或 a = a \\ a 異或 a = 0 \\ 交換律:a 異或 b = b 異或 a \\ 結合律:(a異或b)異或c = a異或(b異或c)

代碼:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        return singleNumberWayIII(nums);
    }
    // way1:map統計;時間複雜度爲o(nlogn),空間複雜度爲o(n).
    int singleNumberWayI(vector<int>& nums) {
        int n = nums.size();
        if(n == 0){
            return -1;
        }
        // count by map
        map<int,int> m;
        for(int num:nums){
            m[num]++;
        }
        int res = -1;
        // return when count = 1
        for(map<int,int>::iterator it = m.begin();it != m.end(); it++){
            if(it->second == 1){//important:不是*it->second而是it->second
                res = it->first;
                break;
            }
        }
        return res;
    }
    // way2:unordered_map;時間複雜度爲o(nlogn),空間複雜度爲o(n).
    int singleNumberWayII(vector<int>& nums) {
        int n = nums.size();
        if(n == 0){
            return -1;
        }
        // count by unordered_map
        unordered_map<int,int> m;
        for(int num:nums){
            m[num]++;
        }
        // return when count = 1
        int res = -1;
        for(unordered_map<int,int>::iterator it = m.begin(); it != m.end(); it++){
            if(it->second == 1){//important:不是*it->second而是it->second
                res = it->first;
                break;
            }
        }
        return res;
    }
    // way3:異或位運算;時間複雜度爲o(n),空間複雜度爲
    int singleNumberWayIII(vector<int>& nums) {
        int n = nums.size();
        if(n == 0){
            return -1;
        }
        int res = 0;
        for(int num:nums){
            res ^= num;
        }
        return res;
    }
};

137:只出現一次的數II

題目:
給定一個非空整數數組,除了某個元素只出現一次以外,其餘每個元素均出現了三次。找出那個只出現了一次的元素。
求解方法:

  1. map平衡二叉樹。統計每個元素出現的次數,返回次數爲1的元素即可。時間複雜度爲o(nlogn)o(nlogn),空間複雜度爲o(n)o(n).
  2. unordered_map散列表。統計每個元素出現的次數,返回次數爲1的元素即可。時間複雜度爲o(nlogn)o(nlogn),空間複雜度爲o(n)o(n).
  3. 數組去重後數據和的3倍與原數組和的差除以2時間複雜度爲o(nlogn)o(nlogn),空間複雜度爲o(n)o(n)。原因是假設數組共3k+13k+1個數,其和記作sum1sum1,去重剩餘k+1k+1個數,其和記作sum2sum2.去重後數組和的三倍即3sum23sum23k+33k+3個數的和,減去3n+13n+1個數的和,剩餘的就是隻出現一次的數的兩倍。即:
    res=(3sum2sum1)2res = \frac{(3*sum2-sum1)}{2}
  4. 三進制不進位加法。上一題中利用異或運算,異或也稱作二進制不進位加法,此題只要利用三進制不進位加法即可。時間複雜度爲o(n)o(n),空間複雜度爲o(1)o(1).

注意事項:

  1. 第三種利用數組和的方法,在求和的時候和藥用long類型的數據,否則會越界.
  2. set和map<int,int>以及unordered_map<int,int>的迭代器的使用的不同方法。

代碼:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
		return singleNumberWayIV(nums);
	}
    // way1:map統計;時間複雜度爲o(nlogn),空間複雜度爲o(n).
    int singleNumberWayI(vector<int>& nums) {
        int n = nums.size();
        if(n == 0){
            return -1;
        }
        // count by map
        map<int,int> m;
        for(int num:nums){
            m[num]++;
        }
        int res = -1;
        // return when count = 1
        for(map<int,int>::iterator it = m.begin();it != m.end(); it++){
            if(it->second == 1){//important:不是*it->second而是it->second
                res = it->first;
                break;
            }
        }
        return res;
    }
    // way2:unordered_map;時間複雜度爲o(nlogn),空間複雜度爲o(n).
    int singleNumberWayII(vector<int>& nums) {
        int n = nums.size();
        if(n == 0){
            return -1;
        }
        // count by unordered_map
        unordered_map<int,int> m;
        for(int num:nums){
            m[num]++;
        }
        // return when count = 1
        int res = -1;
        for(unordered_map<int,int>::iterator it = m.begin(); it != m.end(); it++){
            if(it->second == 1){//important:不是*it->second而是it->second
                res = it->first;
                break;
            }
        }
        return res;
    }
    // way3:sum of array of no duplication and sum of array;時間複雜度爲o(nlogn),空間複雜度爲o(n).
    int singleNumberWayIII(vector<int>& nums) {
        int n = nums.size();
        if(n == 0){
            return -1;
        }
        // get sum of array and remove the duplications meanwhile
        set<int> s;
        long sum1 = 0;//important:long類型
        for(int num:nums){
            sum1 += num;
            s.insert(num);
        }
        // get sum of no duplications
        long sum2 = 0;
        for(set<int>::iterator it = s.begin(); it != s.end(); it++){
            sum2 += (*it);//important:是*it不是it
        }
        return (3*sum2 - sum1)/2;
    }
    // way4:三進制不進位加法;時間複雜度爲o(n),空間複雜度爲o(1).
    /*(1)用 one 記錄到當前處理的元素爲止,二進制1出現“1次”(mod 3 之後的 1)的有哪些二進制位;
      (2)用 two 記錄到當前計算的變量爲止,二進制1出現“2次”(mod 3 之後的 2)的有哪些二進制位。
      (3)當 one 和 two 中的 某一位 同時爲1時表示該二進制位上1出現了3次,此時需要清零。即用二進制模擬三
		進制運算。
	  (4)最終 one 記錄的是最終結果。
    */
    int singleNumberWayIV(vector<int>& nums) {
        int one = 0,two = 0, three = 0;
        for(int num:nums){
            one = one ^ num & ~two;
            two = two ^ num & ~one;
        }
        return one;//important:返回的是one(因爲需要返回的是出現一次的數)
    }
    
};

260:只出現一次的數III

題目:
給定一個整數數組 nums,其中恰好有兩個元素只出現一次,其餘所有元素均出現兩次。 找出只出現一次的那兩個元素。
求解方法:

  1. 排序。(1)判斷第一個元素和最後一個元素;(2)判斷中間元素,只出現一次的元素的特點是既不等於前面的數也不等於後面的數。時間複雜度爲o(nlogn)o(nlogn),空間複雜度爲o(n)o(n).
  2. unordered_map散列表。統計每個元素出現的次數,返回次數爲1的元素即可。時間複雜度爲o(nlogn)o(nlogn),空間複雜度爲o(n)o(n).
  3. 位運算1。(1)通過異或運算找到只出現一次的兩個元素的異或值記作mask = a^b,a和b因爲不同,所以a和b的二進制中至少存在一位不同,即mask中至少存在一個1;(2)通過mask = mask & (-mask)找到mask中最右邊的1;(3)通過mask對這兩個數分組,找到兩個數並更新結果。時間複雜度爲o(nlogn)o(nlogn),空間複雜度爲o(n)o(n)
  4. 位運算2。(1)通過異或運算找到只出現一次的兩個元素的異或值記作mask = a^b,a和b因爲不同,所以a和b的二進制中至少存在一位不同,即mask中至少存在一個1;(2)通過mask = mask & (~mask+1)找到mask中最右邊的1;(3)通過mask對這兩個數分組,找到兩個數並更新結果。。時間複雜度爲o(n)o(n),空間複雜度爲o(1)o(1).

注意事項:

代碼:

class Solution {
public:
	vector<int> singleNumber(vector<int>& nums) {
		return singleNumberWayIV(nums);
	}
    //way1:sort+check;時間複雜度爲o(nlogn),空間複雜度爲o(1);
    /*
        (1)判斷第一個元素和最後一個元素;
        (2)判斷中間元素,只出現一次的元素的特點是既不等於前面的數也不等於後面的數。
    */
    vector<int> singleNumberWayI(vector<int>& nums) {
        int n = nums.size();
        vector<int> res;
        if(n == 0){
            return res;
        }
        //sort
        sort(nums.begin(),nums.end());
        
        
        // check the first number
        if(nums[0] != nums[1]){
            res.push_back(nums[0]);
        }
        // check the last number
        if(nums[n-1] != nums[n-2]){
            res.push_back(nums[n-1]);
        }
        // check the middle element
        for(int i=1; i<n-1;i++){
            if(nums[i]!=nums[i-1] && nums[i]!=nums[i+1]){
                res.push_back(nums[i]);
            }
        }
        return res;
    }
    // way2:unordered_map;時間複雜度爲o(nlogn),空間複雜度爲o(n).
    vector<int> singleNumberWayII(vector<int>& nums) {
        int n = nums.size();
        vector<int> res;
        if(n == 0){
            return res;
        }
        // count
        unordered_map<int,int> m;
        for(int num:nums){
            m[num]++;
        }
        // push to the res when count = 1
        for(unordered_map<int,int>::iterator it = m.begin(); it != m.end(); it++){
            if(it->second == 1){
                res.push_back(it->first);
            }
        }
        return res;
    }
    // way3:位運算;use n = n & (-n) to find the first 1 from right;時間複雜度爲o(n),空間複雜度爲o(1).
    vector<int> singleNumberWayIII(vector<int>& nums) {
        int n = nums.size();
        vector<int> res(2,0);
        if(n == 0){
            return res;
        }
        // 異或
        int mask = 0;
        for(int num:nums){
            mask ^= num;
        }
        // find first 1 from right
        mask = mask & (-mask);
        
        // find the result
        for(int num:nums){
            if((mask & num) == mask){
                res[0] = res[0] ^ num;
            }else{
                res[1] = res[1] ^ num;
            }
        }
        return res;
    }
    // way4:位運算;use n = n & (~n+1) to find the first 1 from right;時間複雜度爲o(n),空間複雜度爲o(1).
    vector<int> singleNumberWayIV(vector<int>& nums) {
        int n = nums.size();
        vector<int> res(2,0);
        if(n == 0){
            return res;
        }
        // 異或
        int mask = 0;
        for(int num:nums){
            mask ^= num;
        }
        // find first 1 from right
        mask = mask & (~mask+1);
        
        // find the result
        for(int num:nums){
            if((mask & num) == mask){
                res[0] = res[0] ^ num;
            }else{
                res[1] = res[1] ^ num;
            }
        }
        return res;
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章