前言
昨天给大家写了两篇解题思路,分别是考察二维前缀和的[LeetCode0304题二维区域和检索 - 矩阵不可变] | 刷题打卡以及考察双指针的[LeetCode11题盛最多水的容器] | 刷题打卡,大家可以看下,觉得有不对的地方或者有更好的思路的可以放在评论区❤️❤️❤️
题目描述
今天要做的题目是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)来执行此操作。
解题思路
看过题目要求之后,可能是我太菜了吧,就想到直接运算,这道题就是求每个数n的二进制中1的个数,则分解如下:
我们需要一个求n的二进制1的个数的函数NumberOf1;
如果n === 0,则直接返回0;
定义一个计数器count = 0;
利用位运算并与1进行&运算:
(n >>> i & 1) === 1
如果符合条件则count++;
这样的话我的解题方法就出来了!
解题代码
var countBits = function(num) {
let res = [];
for (let i = 0; i <= num; i++) {
res.push(NumberOf1(i))
}
return res;
};
function NumberOf1(n) {
if (n === 0) return 0;
let count = 0;
for (let i=0;i<32;i++){
if ((n>>>i & 1) ===1){ //右移并与1进行&运算
count++;
}
}
return count;
}
有意思的思路
有了自己的解题方法后我就开始看题解区,看看大佬们的思路是如何的,结果官方题解上来就是四种解法,唉!又是凑数的一天!
但是,我在题解区看到一个让我看完只能说🐂🍺的解法,贴出来给大家欣赏一下:
思路
对于所有的数字,只有两类:
奇数:二进制表示中,奇数一定比前面那个偶数多一个 1,因为多的就是最低位的 1。
举例:
0 = 0 1 = 1
2 = 10 3 = 11
偶数:二进制表示中,偶数中 1 的个数一定和除以 2 之后的那个数一样多。因为最低位是 0,除以 2 就是右移一位,也就是把那个 0 抹掉而已,所以 1 的个数是不变的。
举例:
2 = 10 4 = 100 8 = 1000
3 = 11 6 = 110 12 = 1100
另外,0 的 1 个数为 0,于是就可以根据奇偶性开始遍历计算了。
代码
vector<int> countBits(int num) {
vector<int> result(num+1);
result[0] = 0;
for(int i = 1; i <= num; i++)
{
if(i % 2 == 1)
{
result[i] = result[i-1] + 1;
}
else
{
result[i] = result[i/2];
}
}
return result;
}
真的是太强了!!!
总结
看到题目之后发现自己看不懂也不要怕,一遍看不懂就多看几遍,想不到解法、没有解题思路也不要觉得 啊!我好菜啊!,可以尝试着去分解一下,一步一步来,先定个小目标,求出第一步。有了开头,后续的思路就比较容易了。
当然,好不容易写出来一提交发现报错或者超出时间限制也不要沮丧,自己仔细的思考过,有过思路,再去看官方题解或者别的大佬的题解也就能够容易理解别人的每一步是如何想的,如何写的。
加油!hxdm!!!
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情