目录
位运算 Tips
1. 概览
符号 | 描述 | 运算规则 |
---|---|---|
& | 与 | 两个位都为1时,结果才为1 |
| | 或 | 两个位都为0时,结果才为0 |
^ | 异或 | 两个位相同为0,相异为1 |
~ | 取反 | 0变1,1变0 |
<< | 左移 | 各二进位全部左移若干位,高位丢弃,低位补0 |
>> | 右移 | 各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,补符号位有的补0 |
2. 异或的逆运算是其本身*****
题目:解码异或后的数组
https://leetcode-cn.com/problems/decode-xored-array/
A & B = C => A & C = B, B & C = A
题目:交换数字
https://leetcode-cn.com/problems/swap-numbers-lcci/
不用临时变量来交换数字,利用异或的逆运算是其本身来做
a = a^b
b = b^a 此时b变成了a
a = a^b 此时a变成了b
3. 汉明距离
题目:汉明距离
https://leetcode-cn.com/problems/hamming-distance/submissions/
两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。给出两个整数 x 和 y,计算它们之间的汉明距离。先两数异或,再逐位取出,计算1的数目。
4. 位运算比较*****
题目:最大数值
https://leetcode-cn.com/problems/maximum-lcci/
编写一个方法,找出两个数字a和b中最大的那一个。不得使用if-else或其他比较运算符。
考虑的点:
- 两个int相减若用int保存可能会溢出, 故用long保存结果(64位)
- 计算机中是算数右移, 高位补符号位, 所以负数右移63位结果是-1
- 只要通过右移63为是否为-1来判断差值的正负号即可
class Solution {
// a < b:
// 1 + (a - b) >> 31 == 0
// a > b:
// 1 + (a - b) >> 31 == 1
public:
int maximum(int a, int b) {
long c = a, d = b;
int k = 1 + ((c - d) >> 63);
return k * a + (!k) * b;
}
};
5. 只出现一次的数字
题目:只出现一次的数字
https://leetcode-cn.com/problems/single-number/
数组里所有元素都有两次,只有一个数字有一次。要求线性复杂度和O(1)额外空间。思路:全员异或,结果=nums[0]^nums[1] ^… ^nums[n-1],这样同样的两个数自己异或自己就抵消,最后只会剩下唯一一个只出现一次的数字。
6. 取二进制奇数位/偶数位*****
题目:配对交换
https://leetcode-cn.com/problems/exchange-lcci/
取奇数位只要让原数和0b010101010101…0101(题目条件为32位数字)做&运算即可,这个数可以写成16进制0x55555555;同理,取偶数位只要让原数和0b1010…1010做&运算即可,这个数可以写成0xaaaaaaaa;取完后奇数位左移一位,偶数位右移一位,二者或运算即可。
class Solution {
public:
int exchangeBits(int num) {
int a = 0xaaaaaaaa; // 取偶数位
int b = 0x55555555; // 取奇数位
return (num & b) << 1 | (num & a) >> 1;
}
};
7. 位运算加法*****
题目:不用加法的加法
https://leetcode-cn.com/problems/add-without-plus-lcci/submissions/
逐位相加,加法:该位用异或(1+0/0+1->1),进位用与(1+1->进位1)
class Solution {
public:
int add(int a, int b) {
while(b != 0){
auto carry = (unsigned int)(a & b) << 1;
a ^= b;
b = carry;
}
return a;
}
};