给定一个非负整数 n
,请计算 0
到 n
之间的每个数字的二进制表示中 1 的个数,并输出一个数组。
一、
看到题目,想到判断一个数是否是2的幂的方法,可以利用位与方法。n&(n-1)可以将n二进制表示的最低位1移除,则可以利用此判断1的个数。
class Solution:
def countBits(self, n: int) -> List[int]:
count = [0]
cnt = 0
for i in range(1,n+1):
c = i
while c:
c &= c-1
cnt += 1
count.append(cnt)
cnt = 0
return count
一定会有0 —— 0个1,所以设置初始数组为0,数[1,n]中每次移除数目中的1,当移除完毕即为1的个数,依次添加数目在数组里并初始化计数变量。
时间复杂度O(nlogn),空间复杂度O(1)
看到官方也是这个方法,但官方更为简洁,直接定义一个判1函数,调用n次。
二、
可以对时间复杂度进行优化,利用动态规划思路。
奇数中,奇数比前一位偶数多一个1,即最低位1,dp[i] = dp[i-1]
偶数中,偶数与其自身一半的偶数的1的个数相同,因为末尾为0,右移一位仅抹零,dp[i] = dp[i/2]
综合一下是dp[i] = = dp[i/2] + i % 2,转换为位运算,i/2 —— i >>1,i % 2 —— i &1,则有:
class Solution:
def countBits(self, n: int) -> List[int]:
count = [0]
for i in range(1,n+1):
count.append(count[i>>1] + (i & 1))
return count
时间复杂度O(n),空间复杂度O(1)
三、
官方有一种比较巧妙的动态规划——最低设置位
因为i & (i-1) 将最低位1置0,则i 比i & (i-1)的1的个数多1,即有:
class Solution:
def countBits(self, n: int) -> List[int]:
count = [0]
for i in range(1,n+1):
count.append(count[i&(i-1)] + 1)
return count
时间复杂度O(n),空间复杂度O(1)