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)来执行此操作。
思路
枚举
题目要求我们求得1~num中每个数转换为二进制后1的个数。对于每一个数,我们可以通过枚举32位来计算第i个数的第k位是否为1
时间复杂度: O(32N)
/**
* @param {number} num
* @return {number[]}
*/
var countBits = function(num) {
const ans = Array.from({length: num + 1}, () => 0);
for(let i = 1; i <= num; ++ i)
for(let k = 0; k < 32; ++ k)
if(i >> k & 1)
ans[i] ++;
return ans;
};
dp
假设f[i]表示第i个数二进制表示时1的个数。对于任意一个合理十进制数字,它二进制表达从左往右的第一个数肯定是1(任何一个数不存在前导0).那么f[i]可以从该二进制表达式中下一个1表达的数字转移过来。
eg:
5的二进制为101, 那么f[5] = f[1] + 1(101下一个1出现的位置代表1)
34的二进制位10010, 那么f[34] = f[2] + 1(10010下一个1出现的位置代表2)
因此f[i] = f[i下个1出现位置对应的值] + 1
那么i下个1出现位置对应的值怎么求呢?i下个1出现位置对应的值 = i - 最高位1对应的值
最高位1对应的值 = floor(log2(i)),这个公式很容易得到,令k = 最高位1对应的位数, 由2 ^ k = (i最高位1对应的值)可算出
综上,我们可以得到转移公式: f[i] = f[i - floor(log2(i))] + 1
时间复杂度: O(N)
/**
* @param {number} num
* @return {number[]}
*/
var countBits = function(num) {
const f = Array.from({length: num + 1});
f[0] = 0;
for(let i = 1; i <= num; ++ i) {
let k = Math.log2(i) >> 0;
f[i] = 1 + f[i - (1 << k)];
}
return f;
};
本文正在参与「掘金 3 月闯关活动」,点击查看活动详情