前n个数字二进制中1的个数


给定一个非负整数 n ,请计算 0n 之间的每个数字的二进制表示中 1 的个数,并输出一个数组。

1.一般思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public:
vector<int> countBits(int n) {
vector<int> ans;
for (int i = 0; i <= n; ++i) {
int x = i;
int count = 0;
while (x) {
if (x % 2 == 1) count++;
x /= 2;
}
ans.push_back(count);
}
return ans;
}
};

2.位运算

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Solution {
public:
vector<int> countBits(int n) {
vector<int> ans;
for (int i = 0; i <= n; ++i) {
int count = 0;
int x = i;
while (x != 0) {
count++;
x = x & (x - 1);
}
ans.push_back(count);
}
return ans;
}
};

3.动态规划

对于所有的数字,只有奇数和偶数两种:

  • 奇数:二进制表示中,奇数一定比前面那个偶数多一个 1,因为多的就是最低位的 1
  • 偶数:二进制表示中,偶数中 1 的个数一定和除以 2 之后的那个数一样多。因为最低位是 0,除以 2 就是右移一位,也就是把那个 0 抹掉而已,所以 1 的个数是不变的

所以我们可以得到如下的状态转移方程:

  • dp[i] = dp[i-1],当i为奇数
  • dp[i] = dp[i/2],当i为偶数
  • dp[i] = dp[i/2] + i % 2:进一步合并为

通过位运算进一步优化:

  • i / 2 可以通过 i >> 1 得到;
  • i % 2 可以通过 i & 1 得到;
1
2
3
4
5
6
7
8
class Solution {
public:
vector<int> countBits(int n) {
vector<int> ans(n + 1, 0);
for (int i = 0; i <= n; ++i) ans[i] = ans[i >> 1] + (i & 1);
return ans;
}
};