一、题目信息
cpp
给你一个整数 n ,对于 0 <= i <= n 中的每个 i ,计算其二进制表示中 1 的个数 ,返回一个长度为 n + 1 的数组 ans 作为答案。
不要使用内置函数来解决(例如,C++ 中的 __builtin_popcount)。
示例 1:
输入:n = 2
输出:[0,1,1]
解释:
0 --> 0
1 --> 1
2 --> 10
示例 2:
输入:n = 5
输出:[0,1,1,2,1,2]
解释:
0 --> 0
1 --> 1
2 --> 10
3 --> 11
4 --> 100
5 --> 101
提示:
0 <= n <= 105
进阶:
很容易就能实现时间复杂度为 O(n log n) 的解决方案,你可以在线性时间复杂度 O(n) 内用一趟扫描解决此问题吗?
二、题解1:朴素解法
1、解题思路:二进制转换法
-
题目要求计算一个十进制整数的二进制形式中1的数字个数
-
使用for循环遍历每个数字,然后对遍历到的每个数字进行二进制转换,将十进制转换成对应二进制
-
在转换的过程中,记录数字1个个数
2、图解

3、代码实现
cpp
vector<int> countBits(int n)
{
vector<int> res(n + 1, 0);
string binaryStr = ""; // 数字i对应的二进制结果值
for (int i = 0; i <= n; i++)
{
int num = i;
binaryStr = "";
while (num != 0)
{
// 十进制数字num转换成二进制
int remainder = num % 2; // 取余数
int business = num / 2; // 商
if (remainder == 1)
{
res[i]++;
}
num = business;
binaryStr = to_string(remainder) + binaryStr;
}
std::cout << i << " -- " << binaryStr << std::endl;
}
return res;
}
4、时间复杂度
- NlogN ,因为遍历需要计算n次,对每个数字i进行二进制转换需要logN次,结果相乘
三、题解2:位运算&解法
1、解题思路
-
通过位运算符&,求i&(i-1)的结果值,会将数字i的二进制最右侧的数字1消除掉
-
这样不断重复这个操作,将所有二进制形式的1消除掉,记录循环的次数就是对应二进制中1的个数
2、图解

3、代码实现
cpp
vector<int> countBits(int n)
{
vector<int> res(n + 1, 0);
for (int i = 0; i <= n; i++)
{
int num = i;
while (num != 0)
{
num = num & (num - 1);
res[i]++;
}
}
return res;
}
4、时间复杂度
- NlogN ,每个数字i的二进制1个数为logn的个数,要找出所有1的个数
四、题解3:位移解法
1、解题思路
-
十进制数字i,要求他的一半大小,只需要将他的二进制形式往右边位移一位
-
如果这个数字i是偶数的话,那他二进制最右侧的数字是0,i的二进制中1的数字个数,和i/2的二进制中1的数字个数相同。
-
如果数字i是奇数的话, 对应二进制最右侧的数字肯定是1,那i的二进制往右位移一位后,得到i/2的二进制1的个数比i的二进制1的个数少1.
-
可以通过这个规律来求解i的二进制中1的个数的值。
2、图解

3、代码实现
cpp
vector<int> countBits(int n)
{
vector<int> res(n + 1, 0);
for (int i = 1; i <= n; i++)
{
res[i] = res[i / 2] + (i & 1);
}
return res;
}
4、时间复杂度
- O(N),动态规划解法,首先要找数字i与i/2的规律,再根据他们的关系来一步步求解