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