數據結構和算法筆記(二):位運算

位運算

位異或

  1. 異或的性質:兩個數字異或的結果a^b是將 a 和 b 的二進制每一位進行運算,得出的數字。 運算的邏輯是如果同一位的數字相同則爲 0,不同則爲 1。
  2. 異或的規律:(1)任何數和本身異或則爲0;(2)任何數和 0 異或是本身;(3)異或滿足交換律。 即 a ^ b ^ c ,等價於 a ^ c ^ b。

只出現一次的數字(其餘都出現兩次) -> (1)可以用unordered_map記錄數組元素出現的次數;(2)更快的方式是直接對所有元素進行異或操作,由於其他元素都出現兩次(任何數和本身異或則爲0),而且異或滿足交換律,所以其他元素異或的結果應該爲0,最後結果爲這個只出現一次的數。

    int singleNumber(vector<int>& nums) {
        int k = 0; // 性質2,任何數和0異或是本身,所以初始值取0
        for(int i = 0; i < nums.size(); i++){
            k^=nums[i];
        }
        return k;
    }

找到數組中兩個只出現一次的數(其餘都出現兩次)

    vector<int> singleNumbers(vector<int>& nums) {
        int k = 0;
        for(int num:nums){
            k^=num;
        }
        // 假設這兩個數字爲a和b,此時k=a^b
        // 將nums裏的數字分兩組,目標是a和b在不同的組裏
        int pos = 0, a = 0, b = 0;
        for(int i = 0; i < 32;i++){
            if(k & 1 == 1){ // 找到k中爲1的位,說明a和b在該位是不同的
                pos = i;
                break;
            }
            k = k >> 1;
        }
        // 根據找到的pos進行分組,a和b在不同組
        for(int num:nums){
            if((num >> pos) & 1 == 1){
                a^=num;
            }else{
                b^=num;
            }
        }
        vector<int> v{a,b};
        return v;
    }

找到數組中只出現一次的數(其餘都出現三次) -> (1) 哈希表存儲和查找,使用unordered_map;(2) 如果某個數字出現3次,那麼這個3個數字的和肯定能被3整除,則其對應二進制位的每一位的和也能被3整除;統計數組中每個數字的二進制中每一位的和,判斷該和是否能被3整除。若可以,則只出現一次的數字的二進制數中那一位爲0,否則爲1。參考

    int singleNumber(vector<int>& nums) {
        int k = 0;
        for(int i = 0; i < 32; i++){
            int count = 0;
            for(int num:nums){
                if((num>>i)&1 == 1){
                    count++;
                }
            }
            // 出現三次的所有數字的每一位上的和都能被3整除,如果count%3==1表明這個只出現一次的數在這一位爲1,否則爲0
            if(count%3==1){ 
                k += 1<<i;
            }
        }
        return k;
    }

相關練習

二進制中1的個數
不用加減乘除做加法 -> 位異或操作可以模擬二進制無進位的加法,位與操作可以模擬二進制的進位(位與的結果需要左移1位)。a可以當做無進位的和,b當做進位,然後不斷重複之前的操作,直到進位b爲0,a便是和。

    int add(int a, int b) {
        int sum, carry;
        while(b!=0){
            sum = a^b;
            carry = (unsigned int)(a&b) << 1;  // C++負數無法左移,需要轉換成無符號整型
            a = sum;
            b = carry;
        }
        return a;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章