前言
按位与运算(&)的一个性质是:对于任意整数 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 春招闯关活动」, 点击查看 活动详情
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!