位运算(Bitwise operations)
数据结构简介…
实战中常用的位运算操作:
位运算 | 描述 |
---|---|
x & 1 == 1 OR == 0 | 判断奇偶 |
x = x & (x - 1) | 清零最低位的1 |
x & -x | 得到最低位的1 |
高频面试题
1.位1的个数
🚀题目链接:LeetCode191.位1的个数
题目:
编写一个函数,输入是一个无符号整数(以二进制串的形式),返回其二进制表达式中数字位数为 ‘1’ 的个数(也被称为汉明重量)。提示:
- 请注意,在某些语言(如 Java)中,没有无符号整数类型。在这种情况下,输入和输出都将被指定为有符号整数类型,并且不应影响您的实现,因为无论整数是有符号的还是无符号的,其内部的二进制表示形式都是相同的。
- 在 Java 中,编译器使用二进制补码记法来表示有符号整数。因此,在上面的 示例 3 中,输入表示有符号整数 -3。
示例 1:
输入:00000000000000000000000000001011 输出:3 解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。
示例 2:
输入:00000000000000000000000010000000 输出:1 解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。
示例 3:
输入:11111111111111111111111111111101 输出:31 解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。
🍬C++ AC代码:
class Solution {
public:
int hammingWeight(uint32_t n) {
int cnt = 0;
while (n != 0) {
n = n & (n - 1);
cnt++;
}
return cnt;
}
};
☕Java AC代码:
public class Solution {
// you need to treat n as an unsigned value
public int hammingWeight(int n) {
int cnt = 0;
while (n != 0) {
n = n & (n - 1);
cnt++;
}
return cnt;
}
}
🍦Python AC代码:
class Solution(object):
def hammingWeight(self, n):
cnt = 0
while n != 0:
n, cnt = n & (n - 1), cnt + 1
return cnt
2. 2
的幂
🚀题目链接:LeetCode231.2 的幂
题目:
给你一个整数
n
,请你判断该整数是否是2
的幂次方。如果是,返回true
;否则,返回false
。
如果存在一个整数x
使得n == 2x
,则认为n
是2
的幂次方。示例 1:
输入:n = 1 输出:true 解释:20 = 1
示例 2:
输入:n = 16 输出:true 解释:24 = 16
示例 3:
输入:n = 3 输出:false
✨Tips:
- ⭐二进制中如果一个数是
2
的幂次方,那么二进制位中有且仅有一个1
。 - ⭐上面一题中使用的二进制运算
x & (x-1)
可以去掉x
中二进制位的最后一个1
,本题我们可以借助它来判断给定的数二进制中是不是只有一个1
。 - ⭐这种方法时间复杂度只需要 O ( 1 ) O(1) O(1)。
🍬C++ AC代码:
class Solution {
public:
bool isPowerOfTwo(int n) {
return n != 0 && (n & ((long)n - 1)) == 0;
}
};
☕Java AC代码:
class Solution {
public boolean isPowerOfTwo(int n) {
return n != 0 && (n & ((long)n - 1)) == 0;
}
}
🍦Python AC代码:
class Solution(object):
def isPowerOfTwo(self, n):
return n != 0 and (n & (n - 1)) == 0
3.比特位计数
🚀题目链接:LeetCode337.比特位计数
题目:
给你一个整数n
,对于0 <= i <= n
中的每个i
,计算其二进制表示中1
的个数 ,返回一个长度为n + 1
的数组ans
作为答案。示例 1:
输入:n = 2 输出:[0,1,1] 解释: 0 --> 0 1 --> 1 2 --> 10
示例 2:
输入:n = 5 输出:[0,1,1,2,1,2] 解释: 0 --> 0 1 --> 1 2 --> 10 3 --> 11 4 --> 100 5 --> 101
✨Tips:
- ⭐还是上面两道题的办法,我们知道二进制运算
i & (i-1)
可以去掉i
中二进制位的最后一个1
。 - ⭐初始化一个
count[n+1]
数组保存1~n
每个数的二进制有多少个1
,使用递推公式count[i] = count[i & (i-1)] + 1
计算每一个数有多少个二进制1
。 - ⭐
i & (i-1)
肯定小于i
,因此我们只需要得到i
去掉二进制位最后一个1
所对应的数有多少个二进制1
,然后再加上去掉的那一个1
,就是i
的二进制位中1
的个数。
🍬C++ AC代码:
class Solution {
public:
vector<int> countBits(int n) {
vector<int> count(n + 1, 0);
for (int i = 1; i <= n; i++)
count[i] = count[i & (i - 1)] + 1;
return count;
}
};
☕Java AC代码:
class Solution {
public int[] countBits(int n) {
int[] count = new int[n + 1];
for (int i = 1; i <= n; i++)
count[i] = count[i & (i - 1)] + 1;
return count;
}
}
🍦Python AC代码:
class Solution(object):
def countBits(self, n):
count = [0]
for i in range(1, n + 1):
count.append(count[i & (i - 1)] + 1)
return count
🎈PS.N皇后的另外一种解法
🚀题目链接:LeetCode52.N皇后 II
题目:
n 皇后问题 研究的是如何将n
个皇后放置在n × n
的棋盘上,并且使皇后彼此之间不能相互攻击。
给你一个整数n
,返回 n 皇后问题 不同的解决方案的数量。示例 1:
输入:n = 4 输出:2 解释:如上图所示,4 皇后问题存在两个不同的解法。
✨DFS核心代码:
✨Tips:
- ⭐用
col
,diag1
与diag2
的二进制位保存当前行可以被皇后攻击到的位置,二进制的第 i i i位是1
,则表示位置 i i i会被其他皇后攻击,其中col
表示列,diag1
与diag2
代表左斜方向与右斜方向。 - ⭐
col | diag1 | diag2
三个进行或运算,得到当前能被攻击到的位置,再取反,将可以放置皇后的位置置为1
,由于我们只用到二进制位的前n个位置,因此将后面的二进制全部置为0,取反后还要和(1 << N) - 1
相与。 - ⭐
bits & -bits
得到bits
最末尾的1
的位置p
,也就是我们可以放置皇后的位置。 - ⭐递归调用dfs,我们在
p
位置放置了皇后,因此参数要更新,col
,diag1
,diag2
分别或运算即可,同时注意到diag1
与diag2
表示左右斜方向能攻击到的位置,还要多一个左移和右移一位的操作。
🍬C++ AC代码:
class Solution {
public:
int count = 0;
int totalNQueens(int n) {
if (n < 1) return 0;
dfs(n, 0, 0, 0, 0);
return count;
}
void dfs(int n, int row, int cols, int diag1, int diag2){
if (row >= n) {
count++;
return;
}
int bits = (~(cols | diag1 | diag2)) & ((1 << n) - 1);
while (bits > 0) {
int p = bits & -bits;
dfs(n, row + 1, cols | p, (diag1 | p) << 1, (diag2 | p) >> 1);
bits = bits & (bits - 1);
}
}
};
☕Java AC代码:
class Solution {
int count = 0;
public int totalNQueens(int n) {
if (n < 1) return 0;
dfs (n, 0, 0, 0, 0);
return count;
}
public void dfs(int n, int row, int cols, int diag1, int diag2){
if (row >= n) {
count++;
return;
}
int bits = (~(cols | diag1 | diag2)) & ((1 << n) - 1);
while (bits > 0) {
int p = bits & -bits;
dfs(n, row + 1, cols | p, (diag1 | p) << 1, (diag2 | p) >> 1);
bits = bits & (bits - 1);
}
}
}
🍦Python AC代码:
class Solution(object):
def totalNQueens(self, n):
if n < 1: return []
self.count = 0;
self.DFS(n, 0, 0, 0, 0)
return self.count
def DFS(self, n, row, cols, diag1, diag2):
if row >= n:
self.count += 1
return
bits = (~(cols | diag1 | diag2)) & ((1 << n) - 1)
while bits:
p = bits & -bits
self.DFS(n, row + 1, cols | p, (diag1 | p) << 1, (diag2 | p) >> 1)
bits = bits & (bits - 1)
总结