191. 位1的个数
考点
n & (n - 1)
技巧,即Brian Kernighan算法n & -n
技巧掩码技巧
移位技巧
位运算与分治法的综合
bitCount
的实现lowBit
的实现
题解
同下
思路
有若干方法可以实现bitCount
,下面依次列举,便于快速熟悉位运算的各种技巧
掩码计数
最容易想到的办法,就是使用类似计算机网络中的掩码
,与原数逐位比较;这里的掩码
也必须为无符号32位类型,否则会溢出
对于int来说,
正数极值用二进制表达是0111_1111_1111_1111_1111_1111_1111_1111
负数极值用二进制表达是1000_0000_0000_0000_0000_0000_0000_0000
负数以补码的形式存在,-1用二进制表达是1111_1111_1111_1111_1111_1111_1111_1111
1 | class Solution { |
时间复杂度:\(O\left( k
\right)\),其中\(k\)为uint32_t
的位数,在这里是32位
空间复杂度:\(O\left( 1 \right)\)
右移计数
每次判断最低位是否为1,然后将原数右移一位;因为题目说明了原数是无符号32位,所以右移不会新增1的个数
若是负数,右移时高位会补1
1 | class Solution { |
时间复杂度:\(O\left( k
\right)\),其中\(k\)为uint32_t
的位数,在这里是32位
空间复杂度:\(O\left( 1 \right)\)
Brian Kernighan算法
对于任何一个数n
,n & (n - 1)
能够将最低位的1变为0
1 | class Solution { |
时间复杂度:\(O\left( \log n \right)\),循环次数等于\(n\)的二进制位中1的个数
空间复杂度:\(O\left( 1 \right)\)
lowBit
对于任何一个数n
,n & -n
能够获取最低位的1的位置;假设二进制表达为0110
,则lowBit返回0010
1 | class Solution { |
时间复杂度:\(O\left( \log n \right)\),循环次数等于\(n\)的二进制位中1的个数
空间复杂度:\(O\left( 1 \right)\)
分治
参考JDK中Integer.bitCount
的函数原型
1 | class Solution { |
原理类似归并排序的迭代版,每次按照单位长度进行归并:
- 单位长度为1时,
1个数
取决于其本身是否为1 - 单位长度为2,取相邻高低位,它们各自的
1个数
相加,达到一一归并为二
的效果 - 单位长度为4,取相邻且单位长度为2的两部分,它们各自的
1个数
相加,达到二二归并为四
的效果 - 单位长度为8,取相邻且单位长度为4的两部分,它们各自的
1个数
相加,达到四四归并为八
的效果 - 循环同上
假设需要统计二进制序列1011101110
中1的总数,分治过程如下

假设有二进制1110
,求1的个数\(N\);设二进制位编号从左到右分别为a、b、c、d,那么有
\[
N=N_a+N_b+N_c+N_d=N_{a,b}+N_{c,d}
\] 初始单位长度为1,按上面的分治过程,需要一一组合为二
在第一步中,设计掩码0101
,令0101 & 1110 = 0100
取出\(N_b\)和\(N_d\)
再1110 >> 1 = 0111
,0101 & 0111 = 0101
取出\(N_a\)和\(N_c\)
0100 + 0101 = 1001
就能得到\(N_{a,b}\)与\(N_{c,d}\)
第二步中可以设计0011
这一掩码,1001 & 0011 = 0001
取出\(N_{c,d}\)
1001 >> 2 = 0010
,0010 & 0011 = 0010
取出\(N_{a,b}\)
0001 + 0010 = 0011 = 十进制的3
,即为 \[
N=N_{a,b}+N_{c,d}
\] 那么这些掩码的功效你应该能明白了
1 | 0x55555555 0b01010101010101010101010101010101 |
时间复杂度:\(O\left( \log C
\right)\),其中\(C\)为uint32_t
的位数,在这里是32位;大部分情况下,是所有方法中最快的一个
空间复杂度:\(O\left( 1 \right)\)