位运算 | 刷题打卡

345 阅读2分钟

Leetcode 338. 比特位计数,难度:Medium
原题请戳这里

题目描述

给定一个非负整数 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 ≤ i ≤ num 范围中的每个数字 i,然后对每个 i 的二进制表示再进行遍历,这样的时间复杂度是 O(n*sizeof(integer))
题目的提示中写道,如何在线性时间* O(n)* 内用一趟扫描做到? 联想到上面的暴力解法,如何在一次扫描的时候,对每个i求出二进制表示的个数? 即消除暴力解法中对每个 i 的二进制表示再进行遍历这一步骤。 由此可以想到分解成子问题看待。对于 i 来说,如果 i * 2,二进制中1的数量不变,即对应二进制中的左移一位操作 (x << 1) 表示乘2;根据这个方法可以获得偶数中包含1的数量,那么计数中所包含的1的个数即是前面一个偶数的值+1;

可看LeetCode题解

AC代码

// 解法1
/**
 * @param {number} num
 * @return {number[]}
 */
var countBits = function(num) {
    let res = new Array(num + 1).fill(0);
    for (let i = 0; i <= num; i++) {
        res[i] = getCounts(i);
    }
    return res;
};

var getCounts = function(number) {
    let count = 0;
    while (number > 0) {
        number = number & (number - 1);
        count++;
    }
    return count;
}


// 解法2
/**
 * @param {number} num
 * @return {number[]}
 */
var countBits = function (num) {
    const res = new Array(num + 1).fill(0);
    for (let i = 1; i <= num; i++) {
        if (i % 2 === 0) {
            res[i] = res[i / 2];
        } else {
            res[i] = res[i - 1] + 1;
        }
        // res[i] = res[i >> 1] + (i & 1);
    }
    return res;
};

总结

本题相似题目:

用到的技巧

  • 位与运算(&)的一个性质是:对于任意整数 x,令 x = x & (x - 1),该运算将 x 的二进制表示的最后一个 1 变成 0。因此,对 x 重复该操作,直到 x 变成 0,则操作次数即为 x 的「一比特数」。
  • x / 2 可以通过 x >> 2 得到,x 除以 2 的余数可以通过 x & 1 得到。所以上述解法2中的if-else语句可以简化为res[i] = res[i >> 1] + (i & 1);

本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情