338. 比特位计数 | 刷题打卡

1,121 阅读2分钟

前言

按位与运算(&)的一个性质是:对于任意整数 x,令 x=x & (x−1),该运算将 x 二进制表示的最后一个 1 变成 0。 因此,对 x 重复该操作,直到 x 变成 0,则操作次数即为 x 的「一比特数」。

题目描述

给定一个非负整数 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. 利用按位与运算(&)的一个性质

直接用双重循环将每一个 i 计算得出其的「一比特数」

AC代码

 var countBits = function(num) {
     let bits = [];
     let x = 0;
    for(let i = 0; i <= num ; i++) {
        let singleNum = 0
        x = i;
        while(x > 0) {
            x = x & (x-1);
            singleNum++
        }
        bits[i] = singleNum;
    }
    return bits;
};

2. 找到 最低位置的1变成0的数

定义正整数 x 的「最低设置位」为 x 的二进制表示中的最低的 1 所在位。 例如,10 的二进制表示是 1010(2),其最低设置位为 2,对应的二进制表示是 10(2)。

而 i & (i - 1) 就是找到 最低位置的1变成0的数,再用i & (i - 1) 这个数的「一比特数」+1 就得到了 i的「一比特数」。

例如: 7(10)--111(2) 找出最低位置的1变成0的数,即6(10)---110(2)

遍历从 1 到 num的每个正整数 i,计算 bits 的值。最终得到的数组 bits即为答案。

AC代码

var countBits2 = function(num) {
    const bits = new Array(num + 1).fill(0);
    for (let i = 1; i <= num; i++) {
        bits[i] = bits[i & (i - 1)] + 1;
    }
    return bits;
};

3. 进行寻找规律

  • 奇数:二进制表示中,奇数一定比前面那个偶数多一个 1,因为多的就是最低位的 1。
  • 偶数:二进制表示中,偶数中 1 的个数一定和除以 2 之后的那个数一样多

AC代码

var countBits3 = function(num) {
    let bits = [];
    bits[0] = 0;
    for(let i = 1; i <= num; i++)
    {
        if((i & 1) != 0)
        {
            bits[i] = bits[i-1] + 1;
        }
        else
        {
            bits[i] = bits[i/2];
        }
    }
    
    return bits;
}

总结

其实动态规划不是拍脑袋想出来的,大多数情况下都是可以先分析记忆化搜索,然后优化为动态规划。

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