338、比特位计数
给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
示例 1:
输入: 2
输出: [0,1,1]
示例 2:
输入: 5
输出: [0,1,1,2,1,2]
进阶:
给出时间复杂度为O(n*sizeof(integer))的解答非常容易。但你可以在线性时间O(n)内用一趟扫描做到吗?
——————————————————————————————————
要求算法的空间复杂度为O(n)。
——————————————————————————————————
你能进一步完善解法吗?要求在C++或任何其他语言中不使用任何内置函数(如 C++ 中的 __builtin_popcount)来执行此操作。
题解思路:
这个应该是最容易想到的方法,遍历从 0 到 num 的每个数字,统计每个数字的二进制中 1 的个数。
复杂度:
时间复杂度: O(N * sizeof(int))
空间复杂度:O(1),返回数组不计入空间复杂度中。
题解python代码:
class Solution:
def countBits(self, num: int) -> List[int]:
res = []
for i in range(num + 1):
res.append(bin(i).count("1"))
return res
题解思路:
在上面暴力解法中,其实有很多重复的计算,比如当 i = 8 的时候,需要求 i = 4, 2, 1, 0 的情况,而这些取值已经计算过了,此时可以使用记忆化搜索。
所谓记忆化搜索,就是在每次递归函数结束的时候,把计算结果保存起来。这样的话,如果下次递归的时候遇到了同样的输入,则直接从保存的结果中直接查询并返回,不用再次重复计算。
举个例子,比如 i = 8 的时候,需要求 i = 4 的情况,而 i = 4 的情况在之前已经计算过了,因此直接返回 memo[4] 即可。
复杂度:
时间复杂度: O(N),因为遍历了一次,每次求解都可以从之前的记忆化结果中找到。
空间复杂度:O(N),用到了辅助的空间保存结果,空间的结果是 O(N)。
题解python代码:
class Solution(object):
def countBits(self, num):
self.memo = [0] * (num + 1)
res = []
for i in range(num + 1):
res.append(self.count(i))
return res
def count(self, num):
if num == 0:
return 0
if self.memo[num] != 0:
return self.memo[num]
if num % 2 == 1:
res = self.count(num - 1) + 1
else:
res = self.count(num // 2)
self.memo[num] = res
return res
其实很多时候,动态规划的方法都是从记忆化搜索中优化出来的。本题也可以如此。
方法二在记忆化搜索过程中,我们看到其实每次调用递归函数的时候,递归函数只会运行一次,就被 memo 捕获并返回了。那么其实可以去除递归函数,直接从 res 数组中查结果。
同时,优化了一下转移方程的表达式为 answer[i] = answer[i >> 1] + (i & 1) 。
于是得到下面的动态规划的方案。
复杂度:
时间复杂度: O(N)O(N),因为遍历了一次。
空间复杂度:O(1)O(1),返回结果占用的空间不计入空间复杂度中。
题解python代码:
class Solution:
def countBits(self, num):
res = [0] * (num + 1)
for i in range(1, num + 1):
res[i] = res[i >> 1] + (i & 1)
return res
作者:fuxuemingzhu
链接:https://leetcode-cn.com/problems/counting-bits/solution/yi-bu-bu-fen-xi-tui-dao-chu-dong-tai-gui-3yog/
来源:力扣(LeetCode)https://leetcode-cn.com/problems/counting-bits/
题解思路:
可以换一个思路,当计算 i 的「一比特数」时,如果存在 0≤j
令 bits[i] 表示 ii 的「一比特数」,则上述关系可以表示成:[j]+1bits[i]=bits[j]+1。
对于正整数 x,如果可以知道最大的正整数 y,使得 y≤x 且 y 是 2 的整数次幂,则 y 的二进制表示中只有最高位是 1,其余都是 0,此时称 y 为 x 的「最高有效位」。令 z=x−y,显然 0≤z
为了判断一个正整数是不是 2 的整数次幂,可以利用方法一中提到的按位与运算的性质。如果正整数 y 是 2 的整数次幂,则 y 的二进制表示中只有最高位是 1,其余都是 0,因此 y&(y−1)=0。由此可见,正整数 y 是 2 的整数次幂,当且仅当 y&(y−1)=0。
显然,0 的「一比特数」为 0。使用 highBit 表示当前的最高有效位,遍历从 11 到 num 的每个正整数 ii,进行如下操作。
如果 i&(i−1)=0,则令 highBit=i,更新当前的最高有效位。
i 比 i−highBit 的「一比特数」多 11,由于是从小到大遍历每个数,因此遍历到 i 时,i−highBit 的「一比特数」已知,令 bits[i]=bits[i−highBit]+1。
最终得到的数组 bits 即为答案。
题解python代码:
class Solution:
def countBits(self, num: int) -> List[int]:
bits = [0]
highBit = 0
for i in range(1, num + 1):
if i & (i - 1) == 0:
highBit = i
bits.append(bits[i - highBit] + 1)
return bits
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/counting-bits/solution/bi-te-wei-ji-shu-by-leetcode-solution-0t1i/
来源:力扣(LeetCode)https://leetcode-cn.com/problems/counting-bits/