常见位运算的技巧
更详细参考:分享|从集合论到位运算,常见位运算技巧分类总结! - 力扣(LeetCode)
计算一个数的二进制表示中1的个数
假定x是一个32位整型变量
最容易想到的做法如下:
1 | unsigned popCount(int x) |
还可以Brian Kernighan 算法,这个算法说的是对于一个数x,x&(x-1)可以获得x删去二进制表示中最右侧的1后的结果。
1 | unsigned popCount(int x) |
还可以利用与运算的技巧1
2
3
4
5
6
7
8
9unsigned popcount (unsigned x)
{
x = (x & 0x55555555) + ((x >> 1) & 0x55555555);//计算原数[k, k+1]区间内的1的总数,并将结果放在[k,k+1],k=1,3,5,...31
x = (x & 0x33333333) + ((x >> 2) & 0x33333333);//计算原数[k, k+3]区间内1的总数,并将结果放在[k,k+3],k=1,5,9,...29
x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F);//计算原数[k, k+7]区间内1的总数,并将结果放在[k,k+7],k=1,9,17,25
x = (x & 0x00FF00FF) + ((u >> 8) & 0x00FF00FF);//计算原数[k, k+15]区间内1的总数,并将结果放在[k,k+15],k=1,17
x = (x & 0x0000FFFF) + ((x >> 16) & 0x0000FFFF);//计算原数[k, k+31]区间内1的总数,并将结果放在[k,k+31],k=1
return x;
}
上面的x&0x55555555可以将x的偶数位全置0,其余类似,这种做法在位运算中很常见。
lowbit
x&(-x)可以获得x的最低位的1,例如6&(-6)=(110)&(010)=010,举例
1 | int ans = 0; |
lowbit所在的位数
可以用c++内置函数
1 | __builtin_ctz(x&-x) |
删除x的二进制形式中最右边的1,其余保持不变
x&(x-1),可参考如何理解x&(-x)和x&(x-1) - 一只小阿狗 - 博客园 (cnblogs.com),等价于x-x&(-x)
枚举一个数的二进制子集
1 | // 枚举s的二进制子集 |
枚举一定范围内固定大小的子集
1 | def count_trailing_zeros(x): |
一图流
编程语言实现
术语 | Python | Java | C++ |
---|---|---|---|
集合大小(元素个数) | s.bit_count() |
Integer.bitCount(s) |
__builtin_popcount(s) |
二进制长度(减一得到集合中的最大元素) | s.bit_length() |
32-Integer.numberOfLeadingZeros(s) |
32-__builtin_clz(s) |
集合中的最小元素 | (s&-s).bit_length()-1 |
Integer.numberOfTrailingZeros(s) |
__builtin_ctz(s) |