1. 常见位运算总结
1) 基础位运算
2) 给一个数 n,确定它的二进制表示中的第 x 位是 0 还是 1
3) 将一个数 n 的二进制表示的第 x 位修改成 1
4) 将一个数 n 的二进制表示的第 x 位修改成 0
5) 位图的思想
位图的本质就是 哈希表
6) 提取一个数 n 二进制表示中最右侧的 1(lowbit)
7) 干掉一个数 n 二进制表示中最右侧的 1
例题:
191. 位1的个数 - 力扣(LeetCode)
338. 比特位计数 - 力扣(LeetCode)
461. 汉明距离 - 力扣(LeetCode)
8) 位运算的优先级
位运算符太多了,容易记混,所以不记了,能加括号就加括号
9) 异或(^)运算的运算律
例题:
136. 只出现一次的数字 - 力扣(LeetCode)
2. 判定字符是否唯一
题目描述:
解法一:数组模拟哈希表
代码实现:
class Solution {
public boolean isUnique(String astr) {
int n = astr.length();
if (n > 26) return false; //字符串仅包含小写字母
char[] arr = astr.toCharArray();
int[] ret = new int[26];
for (int i = 0; i < n; i++) {
ret[arr[i] - 'a']++;
if (ret[arr[i] - 'a'] > 1) {
return false;
}
}
return true;
}
}
解法二:位图
代码实现:
class Solution {
public boolean isUnique(String astr) {
//鸽巢原理优化
if (astr.length() > 26) return false;
int bitMap = 0;
for (int i = 0; i < astr.length(); i++) {
int x = astr.charAt(i) - 'a';
//判断字符是否已存在位图中
if (((bitMap >> x) & 1) == 1) return false;
//将当前字符加入到位图
bitMap |= 1 << x;
}
return true;
}
}
3. 丢失的数字
题目描述:
解法一:哈希表
//哈希表
class Solution {
public int missingNumber(int[] nums) {
int n = nums.length;
int[] hash = new int[n + 1];
for (int i = 0; i < n; i++) {
hash[nums[i]]++;
}
for (int i = 0; i < hash.length; i++) {
if (hash[i] == 0) {
return i;
}
}
return -1;
}
}
解法二:高斯求和
//高斯求和
class Solution {
public int missingNumber(int[] nums) {
int n = nums.length;
int sum1 = 0;
int sum2 = 0;
for (int i = 0; i < n; i++) {
sum1 += nums[i];
}
for (int i = 0; i <= n; i++) {
sum2 += i;
}
return sum2 - sum1;
}
}
解法三:位运算(异或运算的运算律)
//位运算
class Solution {
public int missingNumber(int[] nums) {
int n = nums.length;
int ret = 0;
for (int i = 0; i < n; i ++) {
ret ^= nums[i];
}
for (int i = 0; i <= n; i++) {
ret ^= i;
}
return ret;
}
}
4. 两整数之和
题目描述:
解法:位运算(异或运算 - 无进位相加)
算法思路:
代码实现:
class Solution {
public int getSum(int a, int b) {
while (b != 0) {
int x = a ^ b; // 计算无进位相加的结果
int y = (a & b) << 1; // 计算进位
a = x;
b = y;
}
return a;
}
}
5. 只出现一次的数字 II
题目描述:
解法:利用位运算依次确定每一个二进制位
代码实现:
class Solution {
public int singleNumber(int[] nums) {
int ret = 0;
for (int i = 0; i < 32; i++) { // 依次修改 ret 中的每一个比特位
int sum = 0;
for (int x : nums) { // 统计 nums 中所有的数的第 i 位二进制的和
if (((x >> i) & 1) == 1) {
sum++;
}
}
sum %= 3;
if (sum == 1) ret |= (1 << i); // 将ret 的第 i 位二进制修改为 1
}
return ret;
}
}
6. 只出现一次的数字III
题目描述:
算法思路:
是下面 7 题的思路的一部分
代码实现:
class Solution {
public int[] singleNumber(int[] nums) {
// 1. 找到 a ^ b
int tmp = 0;
for (int x : nums) {
tmp ^= x;
}
// 2. 找到 tmp 二进制位中最右侧为 1 的位置
int diff = 0;
while (true) {
if (((tmp >> diff) & 1) == 1) {
break;
}
diff++;
}
// 3. 按照 diff 位置为1或0,将数组分为两类分别异或
int[] ret = new int[2];
for (int x : nums) {
if (((x >> diff) & 1) == 1) {
ret[0] ^= x;
} else {
ret[1] ^= x;
}
}
return ret;
}
}
7. 消失的两个数字
题目描述:
算法思路:
结合 268. 丢失的数字 和 260. 只出现一次的数字 III 两题
代码实现:
class Solution {
public int[] missingTwo(int[] nums) {
int n = nums.length;
// 1. 先把所有数异或在一起
int tmp = nums[0];
for (int i = 1; i < n; i++) {
tmp ^= nums[i];
}
for (int i = 0; i <= n + 2; i++) { // 注:此处为 <=
tmp ^= i;
}
// 2. 找到 a,b 两个数比特位不同的位置
int x = -1; // 使用 x 记录该位置
for (int i = 0; i < 32; i++) {
if (((tmp >> i) & 1) == 1) {
x = i;
break;
}
}
// 3. 将所有的数按照 x 位置是0还是1,分为两类异或
int[] ret = new int[2];
for (int i : nums) {
if (((i >> x) & 1) == 1) ret[0] ^= i;
else ret[1] ^= i;
}
for (int i = 1; i <= n + 2; i++) {
if (((i >> x) & 1) == 1) ret[0] ^= i;
else ret[1] ^= i;
}
return ret;
}
}