给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
输入: 2 输出: [0,1,1]
输入: 5 输出: [0,1,1,2,1,2]
暴力破解:
class Solution {
private:
int countNum(int num) {
int res = 0;
while(num >= 1) {
if(num % 2 == 1) res++;
num = num / 2;
}
return res;
}
public:
vector<int> countBits(int n) {
vector<int> bits(n+1,0);
for(int i = 0; i < n+1; i++) {
bits[i] = countNum(i);
}
return bits;
}
};
位运算的改进: 这里已经写过博客
动态规划:学习官方题解
(1)最高有效位
dp[i]
表示i
中[数字1的个数]
,我们的目的是想知道是否存在j
,使得通过dp[j]
求出dp[i]
,0<=j<i
来看规律:
0-----0
1-----1
2-----1
3-----2
4-----1
5-----2
6-----2
7-----3
8-----1
9-----2
10----2
11----3
12----2
13----3
14----3
15----4
16----1
5、6、7
三个数中1
的个数分别是:2 2 3
,刚好由1、2、3
三个数中1的个数相加而来
于是,应该有:dp[i] = dp[j] + 1
,
这里j
的含义应该是什么呢?
我们发现:
1 = 5 - 4
2 = 6 - 4
3 = 7 - 4
显然,这里j
是比数字i
小的最大的
2的整数次幂这个数
官方解答称:j为 i 的「最高有效位」
令k = i - j, dp[i] = dp[k] + 1
代码:
vector<int> countBits(int n) {
vector<int> bits(n+1,0);
int k = 0;
for(int i = 1; i < n + 1; i++) {
if((i & (i-1)) == 0) //如果这个数是2的整数次幂
k = i;
bits[i] = bits[i - k] + 1;
}
return bits;
}
(2)最低有效位
再来看规律:
0-----0
1-----1
2-----1
3-----2
4-----1
5-----2
6-----2
7-----3
8-----1
9-----2
10----2
11----3
12----2
13----3
14----3
15----4
16----1
规律:
(1)奇数的1
的数量比它前面的偶数的1
的数量多1
(2)偶数的1
的数量一定和它二分之一的1
的个数是一样的
- 如果
i
是偶数,则bits[i] = bits[i/2]
- 如果
i
是奇数,则bits[i] = bits[i-1] + 1
两种情况可以合并成:bits[i]
的值等于bits[i/2]
的值加上i
除以2
的余数,bits[i]=bits[i>>1]+(i & 1)
vector<int> countBits(int n) {
vector<int> bits(n+1,0);
bits[0] = 0;
for(int i = 1; i < n + 1; i++) {
if(i & 1) {
bits[i] = bits[i-1] + 1;
}
else {
bits[i] = bits[i/2];
}
}
return bits;
}