定义
- 程序中数据都是以二进制形式存储的,位运算就是直接对二进制位进行操作,处理速度非常快。
符号
符号 | 描述 | 运算规则 |
---|---|---|
& | 与 | 两个位都为1时,结果才为1,其他情况结果为0 |
| | 或 | 两个位都为0时,结果才为0,其他情况结果为1 |
^ | 异或 | 两个位相同时,结果为0,不同时,结果为1 |
~ | 取反 | 0变1,1变0 |
<< | 左移 | 各二进制位左移若干位,高位丢弃,低位补0 |
>> | 右移 | 各二进制位全部右移若干位,对于无符号数,高位补0;对于有符号数, 各编译器处理方法不一样,有的补符号位(算数右移),有的补0(逻辑右移) |
- 逻辑右移就是不考虑符号位,右移一位,左边补零即可。 算术右移需要考虑符号位,右移一位,若符号位为1,就在左边补1;否则就补0。`
有符号数的最高位是符号位,0表示正数,1表示负数,在计算机中,负数以原码的补码形式表达
- 正数的原码,反码,补码都相同。
- 负数的原码是对应正数的原码将符号位从0改为1,负数的反码为对该数的原码除符号位外各位取反,负数的补码为负数的反码再加1。
- 例子:2的8位原码为 00000010,-2的8位原码为 10000010,-2的反码为 11111101,-2的补码为 11111110。
- 20 原码 00010100 -20 10010100 -20 10001011 10001100
异或特点
- x ^ 0 = x
- x ^ 1s = ~x // 1s = ~0
- x ^ (~x) = 1s
- x ^ x = 0
- a ^ b = c => a ^ c = b,b ^ c = a
- a ^ b ^ c = a ^ (b ^ c)= (a ^ b) ^ c
常用
- x & 1 == 1 或 x & 1 == 0 判断奇偶性
- X = X & (X - 1) = > 清零最低位 (因为如果X最后一位是1,则X-1最后一位是0,相与,最后一位得0,如果X的最低位1后面一堆0,比如是 1000 则X-1之后 变为 0111,相与得到 0000)
- X & -X = > 得到最低位的1
更复杂操作
- 将x最右边n位清0:x & (~0 << n)
- 获取x的第n位值(0或者1):(x >> n ) & 1
- 获取x的第n位的幂值: x & (1 << (n-1))
- 仅将第n位置为1:x | (1 << n)
- 仅将第n位置为0:x | ~((1 << n))
- 将x最高位至第n位(含)清零: x & ((1 << n) - 1)
- 将第n位至第0位(含)清零: x & (~((1 << n) - 1))
题目
leetcode 231. 2的幂
func isPowerOfTwo(n int) bool {
// 2的x次幂特点:二进制有且只有一个1。
// 去掉最低位的1,即是去掉所有1,即是等于0。再约束其本身不为0。
return n & (n -1) == 0 && n!=0
}
leetcode 338. 比特位计数
解法1
- 每个数单独计算1的位数
func countBits(num int) []int {
var ret []int
var x,c int
for i := 0; i <=num ; i ++{
x = i
c = 0
for x != 0 {
x = x & (x-1)
c ++
}
ret = append(ret,c)
}
return ret
}
解法2
- 动态规划推导状态转移方程
func countBits(num int) []int {
ret:= make([]int,num+1)
for i := 1; i <=num ; i ++{
ret[i] = ret[i&(i-1)]+1
}
return ret
}
leetcode 52. N皇后 II
func totalNQueens(n int) int {
var count int
var d func(int,int,int,int)
d = func(row, col, pie, na int) {
if row == n {
count++
return
}
bits := (^(col | pie | na)) & ((1 << uint(n)) - 1)
for bits > 0 {
p := bits & -bits
d(row+1, col|p, (pie|p)<<1, (na|p)>>1)
bits &= bits - 1
}
}
d(0, 0, 0, 0)
return count
}