leetcode高頻題筆記之位運算

位運算常見問題

在這裏插入圖片描述
在這裏插入圖片描述

191.位1的個數

在這裏插入圖片描述
解法一:
利用x&1返回最低位是否爲1,不斷的左移1
從最低位一個1開始與,如果不爲0計數加1

public class Solution {
    public int hammingWeight(int n) {
        int x = 1;
        int count = 0;
        for (int i = 0; i < 32; i++) {         
            if ((n & x) != 0) {//和某位的一個1進行與操作,如果不爲0則存在1,計數+1
                count++;
            }
            x = x << 1;//左移一位
        }
        return count;
    } 
}

解法二:
利用x&(x-1)去掉最後一位1,判斷如果去掉最後一位後爲0了可以提前結束,去掉了多少次就去掉了多少個0

public class Solution {
    // you need to treat n as an unsigned value
     public int hammingWeight(int n) {
        int count = 0;
        while (n != 0) {
            count++;
            n &= (n - 1);
        }
        return count;
    }
}

231.2的冪

在這裏插入圖片描述
2的冪值都滿足只有一個1
只有一個1的不一定是2的冪值,排除一個數-2147483648 即 Integer.MIN_VALUE,它的二進制是1個1和31個0,1代表符號位

採用x&(x-1)去除掉最低位的1,如果x&(x-1) == 0則只有一個1是2的冪

public class Solution {
    public boolean isPowerOfTwo(int n) {
        return n != 0 && n != Integer.MIN_VALUE && (n & (n - 1)) == 0;
    }
}

342.4的冪

在這裏插入圖片描述
與2的冪一樣,只有一個1,並且這個1的位置是固定的,是偶數位
所以還需要剔除下在 奇數位的值
0xaaaaaaaa = 0b10101010101010101010101010101010
只要1的位置全爲0,則不符合

public class Solution {
    public boolean isPowerOfFour(int num) {
        return num != 0 && (num & (num - 1)) == 0 && (0xaaaaaaaa & num) == 0;
    }
}

190.顛倒二進制位

在這裏插入圖片描述
思路:返回結果初始化全0,從最低位開始判斷每一位是否爲0,如果不爲0將返回值的31-i位設置爲1

public class Solution {
    public int reverseBits(int n) {
        int x = 1;//用來&操作的變量
        int res = 0;//初始化全0
        for (int i = 0; i < 32; i++) {
            if ((n & x) != 0) {//如果&結果不爲0,表明這一位爲1,需要將res的31-i位設置爲1
                res |= 1 << 31 - i;//或操作使新增的數到指定位置
            }
            x <<= 1;//&操作變量左移一位
        }
        return res;
    }
}

338.比特位計數

在這裏插入圖片描述
解法一:逐個計數

public class Solution {
    public int[] countBits(int num) {
        int res[] = new int[num + 1];
        for (int i = 1; i <= num; i++) {
            int count = 0;//計數器
            int cur = i;//當前值
            while (cur != 0) {//通過去除尾部1的方式計數
                cur &= (cur - 1);
                count++;
            }
            res[i] = count;
        }
        return res;
    }
}

解法二:動態規劃
第i項的值等於去除掉最後一位的值+最後一位和1與的值

public class Solution {
    public int[] countBits(int num) {
        int res[] = new int[num + 1];
        for (int i = 1; i <= num; i++) {
            res[i] = res[i >> 1] + (i & 1);
        }
        return res;
    }
}

解法三:動態規格
第i位的值等於去除掉結尾1後的數量+1

public class Solution {
      public int[] countBits(int num) {
        int res[] = new int[num + 1];
        for (int i = 1; i <= num; i++) {
            res[i] = res[i & (i - 1)] + 1;
        }
        return res;
     }
}

461.漢明距離

在這裏插入圖片描述

  • x異或y獲得不同的位
  • x&(x-1)去除尾部1統計1個數
public class Solution {
    public int hammingDistance(int x, int y) {
        int z = x ^ y;
        int count = 0;
        while (z != 0) {
            z &= (z - 1);
            count++;
        }
        return count;
    }
}

136.只出現一次的數字

在這裏插入圖片描述

  • 如果兩個數相同,異或結果爲0
  • 將所有的數異或,剩下的就是單出來的數
public class Solution {
    public int singleNumber(int[] nums) {
        int res = nums[0];
        for (int i = 1; i < nums.length; i++) {
            res ^= nums[i];
        }
        return res;
    }
}

260.只出現一次的數字III

在這裏插入圖片描述
因爲存在兩個不一樣的,簡單的異或完了肯定不行
首先異或下找到兩個只出現一次的數字的異或,這兩個數字至少有一位不一樣,也就是說異或結果至少有一個1
通過diff&=-diff獲取最右側的那個1,以那個位是否爲1將整個數組的數分成兩堆,一堆那個位置爲1,另一堆那個位置爲0,那麼從兩堆中就可以找出這兩個數了

public class Solution {
    public int[] singleNumber(int[] nums) {
        int diff = 0;
        for (int num : nums) diff ^= num;//得到兩個值的異或

        diff &= -diff;//獲得兩個只出現一次的數字最右邊不同的那一位數
        int[] res = new int[2];
        for (int num : nums) {
            if ((num & diff) == 0) res[0] ^= num;//第一堆,最右側異或爲1位位1
            else res[1] ^= num;//第二堆
        }
        return res;
    }
}

268.缺失數字

在這裏插入圖片描述
已知兩個相同的數異或的結果爲0
爲了找出0-n中丟失的數字,將0-n中的已知n個數字和0-n進行異或,就轉換成找只出現一次數字問題。

public class Solution {
    public int missingNumber(int[] nums) {
        int res = 0;
        for (int i = 0; i < nums.length; i++) {
            res = res ^ i ^ nums[i];
        }
        return res ^ nums.length;
    }
}

面試題16.01.交換數字

在這裏插入圖片描述
非常容易想到,利用a=a+b;b=a;a=a-b可以完成,但是需要考慮越界
用異或實現就不需要考慮邊界問題

a = a ^ b ^b ;
b = b ^ a ^a;

轉化爲代碼就是

public class Solution {
    public int[] swapNumbers(int[] numbers) {
        numbers[0] = numbers[0] ^ numbers[1];//a = a^b
        numbers[1] = numbers[0] ^ numbers[1];//b = a^b^b = a
        numbers[0] = numbers[0] ^ numbers[1];//a = a^b^a^b^b = b
        return numbers;
    }
}

693.交替位二進制數

在這裏插入圖片描述

  • 將n右移一位後與n進行異或操作,a = n&(n>>1)
    如1010右移爲101,異或得1111
    如1011右移爲101,異或得1110
  • 爲了判定a是否爲全1,另flag = a&(a-1)
    如1111加一爲10000,與操作爲0
    如1110加一位1111,與操作爲1110!=0

通過上面兩步就可以判斷一個數是否滿足了

public class Solution {
    public boolean hasAlternatingBits(int n) {
        int a = n ^ (n >> 1);
        return (a & (a + 1)) == 0;
    }
}

476.數字的補數

在這裏插入圖片描述
求補數就是將有效的0和1進行互換,所以只需要一個和num位數相同全1的數進行異或運算就可以了
問題轉換成求一個與num位數相同的全1的數

  • 首先,獲得數mask :01000000000000000000000000000000(mask = 1 << 30
  • 將 mask與num進行與操作,如果爲0一直右移
  • 當num月mask與不爲0是,mark的1所在的位置就是num的最高位的位置
  • mask左移1位-1得到從mask位開始的全1值
  • 全1與num異或得補數
    舉例:
    1101010
    mark = 1000000
    (mark<1)-1 = 1111111
    1111111^1101010 = 0010101
class Solution {
    public int findComplement(int num) {
        if (num == 0) return 1;
        int mask = 1 << 30;//不考慮符號位,就移動到最大位
        while ((mask & num) == 0) mask >>= 1;
        mask = (mask << 1) - 1;
        return mask ^ num;
    }
}

371.兩整數之和

在這裏插入圖片描述
不進位加法a^b
進位數計算(a&b)<<1
舉例:計算12+7
12:1100
7:0111

12^7 = 1011
(12&7)<<1 = 1000

第一輪循環得到以上兩個結果,遞歸調用,求1011+1000

1011^1000 = 0011
(1011&1000)<<1 = 10011

第二輪循環得以上兩個結果,遞歸調用,求0011+10011

00011^10011 = 10000
(00011&10011)<<1 = 00011

第二輪循環得以上兩個結果,遞歸調用,求10000+00011

10000^00011 = 10011
(10000&00011)<<1 = 0

return 10011 獲得結果

迭代代碼

public class Solution {
    public int getSum(int a, int b) {
        while (b != 0) {
            //不進位加法
            int tmp = a ^ b;
            //計算進位值
            int carry = (a & b) << 1;
            a = tmp;
            b = carry;
        }
        return a;
    }
}

遞歸代碼:

public class Solution {
    public int getSum(int a, int b) {
        return b != 0 ? getSum(a ^ b, (a & b) << 1) : a;
    }
}

318.最大單詞長度乘積

在這裏插入圖片描述

用二進制的一位表示某一個字母是否出現過,0表示沒出現,1表示出現。

"abcd"二進制表示00000000 00000000 00000000 00001111

"bc"二進制表示00000000 00000000 00000000 00000110。

當兩個字符串沒有相同的字母時,二進制數與的結果爲0。

public class Solution {
    public int maxProduct(String[] words) {
        int wordsLen = words.length;
        int compare[] = new int[wordsLen];
        //將字符串映射到位
        for (int i = 0; i < wordsLen; i++) {
            for (char c : words[i].toCharArray()) {
                compare[i] |= 1 << (c - 'a');
            }
        }
        //搜索完全不相同的兩個串
        int maxres = 0;
        for (int i = 0; i < wordsLen; i++) {
            for (int j = i + 1; j < wordsLen; j++) {
                if ((compare[i] & compare[j]) == 0) {
                    maxres = Math.max(maxres, words[i].length() * words[j].length());
                }
            }
        }
        return maxres;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章