leetcode338—比特位计数
关键词:位运算 动态规划
给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
示例 1:
输入: 2
输出: [0,1,1]
示例 2:
输入: 5
输出: [0,1,1,2,1,2]
进阶:
- 给出时间复杂度为 O ( n ∗ s i z e o f ( i n t e g e r ) ) O(n*sizeof(integer)) O(n∗sizeof(integer))的解答非常容易。但你可以在线性时间 O ( n ) O(n) O(n)内用一趟扫描做到吗?
- 要求算法的空间复杂度为 O ( n ) O(n) O(n)。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/counting-bits
解法
1. 逐个遍历求解
遍历0到n所有数字,对它们依次求解。
class Solution:
def countBits(self, n: int) -> List[int]:
#每个数都统计一次
count = [0]*(n+1)
for i in range(n+1):
t = 0
num = i
while num!=0:
num = num&(num-1)
t += 1
count[i] = t
return count
复杂度分析:
- 时间复杂度: O ( n log n ) O(n\log n) O(nlogn),对n个整数进行依次求解,每个整数用二进制表示位数不超过 log n \log n logn 即 while 循环次数不会超过 log n \log n logn,所以复杂度不会超过 O ( n log n ) O(n\log n) O(nlogn)。
- 空间复杂度:除了返回数组以外,消耗空间为常数。
2.动态规划
具体原理见 官方题解
方法1—记录2的指数项
每次遍历到2的指数项时,将其记录下来,以2的一个指数项作为一个周期,运用动态规划列出状态转移方程。
h
i
g
h
b
i
t
=
i
=
2
k
highbit = i = 2^k
highbit=i=2k
r
e
s
u
l
t
[
j
]
=
r
e
s
u
l
t
[
j
−
h
i
g
h
b
i
t
]
+
1
result[j] = result[j-highbit] + 1
result[j]=result[j−highbit]+1
下一个周期的1的个数相当于只比当前周期对应位置多了一个最高位的1。
判断2的指数项的方法就是利用i&(i-1)=0
,因为当前只有最高位为0。
class Solution:
def countBits(self, n: int) -> List[int]:
#动态规划1,记录2的指数项
count = []
count.append(0)
for i in range(1, n+1):
# 只有1个1,为2的指数项
if i&(i-1) == 0:
highbit = i
# count[i] = count[i-highbit]+1
count.append(count[i-highbit]+1)
return count
复杂度分析:
- 时间复杂度: O ( n ) O(n) O(n),每个整数只需常数时间计算。
- 空间复杂度:除了返回数组以外,消耗空间为常数。
方法2—二进制右移
将二进制右移,会使得最低位消失。
- 当i为偶数时,最低位为0,右移不影响1的个数。
- 当i为奇数时,最低位为1,右移使得1的个数减少1。
class Solution:
def countBits(self, n: int) -> List[int]:
# 动态规划2 二进制右移
count = [0]*(n+1)
for i in range(n+1):
if i%2 != 0:
# i为奇数时,右移一位会导致末尾1消失,减少1个1
count[i] = count[i>>1] + 1
else:
# i为偶数时,右移一位没有影响
count[i] = count[i>>1]
return count
复杂度分析:
- 时间复杂度: O ( n ) O(n) O(n),每个整数只需常数时间计算。
- 空间复杂度:除了返回数组以外,消耗空间为常数。
方法3—减1再补
利用i&(i-1)只会使得最低一位1变成0的性质,列出状态转移方程:
c o u n t [ i ] = c o u n t [ i & ( i − 1 ) ] + 1 count[i] = count[i\&(i-1)]+1 count[i]=count[i&(i−1)]+1
i中1的个数比i&(i-1)多一个,所以可以这样进行计算。
class Solution:
def countBits(self, n: int) -> List[int]:
#动态规划3,i和i-1进行与运算会将最低一位1消掉,所以最后补上就构成了状态转移方程
count = [0]*(n+1)
for i in range(n+1):
count[i] = count[i&(i-1)]+1
return count
复杂度分析:
- 时间复杂度: O ( n ) O(n) O(n),每个整数只需常数时间计算。
- 空间复杂度:除了返回数组以外,消耗空间为常数。