常见位运算的技巧
更详细参考:分享|从集合论到位运算,常见位运算技巧分类总结! - 力扣(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) | 
