[LeetCode: 338. 比特位计数] | 刷题打卡

166 阅读2分钟

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 月闯关活动」,点击查看活动详情