文章目錄
位運算常見問題
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;
}
}