题目地址
给你一个整数 n ,对于 0 <= i <= n 中的每个 i ,计算其二进制表示中 1 的个数 ,返回一个长度为 n + 1 的数组 ans 作为答案。
示例 1:
输入:n = 2
输出:[0,1,1]
解释:
0 --> 0
1 --> 1
2 --> 10
示例 2:
输入:n = 5
输出:[0,1,1,2,1,2]
解释:
0 --> 0
1 --> 1
2 --> 10
3 --> 11
4 --> 100
5 --> 101
提示:
0 <= n <= 10^5
进阶:
很容易就能实现时间复杂度为 O(n log n) 的解决方案,你可以在线性时间复杂度 O(n) 内用一趟扫描解决此问题吗?
你能不使用任何内置函数解决此问题吗?(如,C++ 中的 __builtin_popcount )
解法:动态规划+位运算
对于正整数x,将其二进制表示右移一位,等价于将其二进制表示的最低位去掉,得到的数是⌊x/2⌋(即x除以2的商向下取整)。如果bits[⌊x/2⌋]的值已知,则可以得到bits[x]的值:
- 如果x是偶数,则bits[x]=bits[⌊x/2⌋];
- 如果 xx 是奇数,则bits[x]=bits[⌊x/2⌋]+1。
上述两种情况可以合并成:bits[x]的值等于bits[⌊x/2⌋]的值加上x除以2的余数。
由于⌊x/2⌋可以通过x>>1得到,x除以2的余数可以通过x&1得到,因此有:bits[x]=bits[x>>1]+(x&1)。
遍历从1到n的每个正整数i,计算bits的值。最终得到的数组bits即为答案。
位运算的基础知识:
n&1:
&运算通常用于二进制取位操作,例如一个数&1的结果就是取二进制的最末位。这可以用来判断一个整数的奇偶,二进制的最末位为0表示该数为偶数,最末位为1表示该数为奇数。
也就是说,n&1 就是——判断n是否为奇数
因为n为奇数时,对应的二进制数最低位一定为1,n&1的结果就是1
n为偶数时,相应的最低位为0,n&1的结果就是0,
这里也可以写 n&1 ==1 或者写 n%2 == 1 或者写 n%2
>>1:
>>1 等价于 /2
<<1 等价于 *2
以n=5为例,跟着代码走一遍:
代码实现:
class Solution:
def countBits(self, n: int) -> List[int]:
bits = [0]
for i in range(1, n + 1):
bits.append(bits[i >> 1] + (i & 1))
return bits