题目一
力扣:338. 比特位计数
思路
常规
对1到n每个数求一下他们中有多少个1即可,这种思路属于直接模拟。时间复杂度较高 : O(nlgn)。
进阶(DP)
我们自己写一些数,观察二进制位变化可得知,这些东西是有规律的,如果每次都重新计算,会造成大量重复计算,所以我们要观察好,利用已知的一些规律,用已经计算过的,来推出新的数的结果,所以想到了DP。
最低设置位
我们现在处理到了第 i 个数,那么0到i-1都是处理过的,第i个数中的1的数量=比i少一个1的数(一定小于i本身,属于已经计算过的)的1的数量+1。
所以递推关系式为:
ans[i] = ans[i&(i - 1)] + 1
(i&(i - 1))的作用就是干掉最右面的第一个1,因为少了最右面的一个1,所以一定小于i。
最高有效位
除了上面的方法,我们还可以通过观察1的变化来推导递推关系式。
最低有效位
1、以上面的图为例,两位的3求好之后,如果我们为其扩充到3位数,那么就左移一位就行,会产生两个新的数,即最后一位是0或者1。
2、从上面就可以知道如何从已求出的数推知新数的方法。所以拿到一个数,先右移一位,找到与之对应的已经求过的数,再加上最后一位即可。
代码
常规
class Solution {
public:
int number(int n) {//复杂度是O(lgN),求1的个数
int ans = 0;
while (n > 0) {
n = n & (n - 1);
ans++;
}
return ans;
}
vector<int> countBits(int n) {
vector<int> ans(n + 1, 0);
for (int i = 1; i <= n; i++) {
int num = number(i);
ans[i] = num;
}
return ans;
}
};
所有代码均以通过力扣测试
(经过多次测试最短时间为):
进阶
1、最低设置位
class Solution {
public:
vector<int> countBits(int n) {
vector<int> ans(n + 1, 0);
for (int i = 1; i <= n; i++) {
ans[i] = ans[i&(i - 1)] + 1;
}
return ans;
}
};
所有代码均以通过力扣测试
(经过多次测试最短时间为):
2、最高有效位
class Solution {
public:
vector<int> countBits(int n) {
vector<int> ans(n + 1, 0);
int front = 0;
for (int i = 1; i <= n; i++) {
if ((i&(i - 1)) == 0) {
front = i;//找到基准数
}
ans[i] = ans[i - front] + 1;
}
return ans;
}
};
所有代码均以通过力扣测试
(经过多次测试最短时间为):
3、最低有效位
class Solution {
public:
vector<int> countBits(int n) {
vector<int> ans(n + 1, 0);
for (int i = 1; i <= n; i++) {
ans[i] = ans[i >> 1] + (i & 1);//位运算优先级低,一定加好括号
}
return ans;
}
};
所有代码均以通过力扣测试
(经过多次测试最短时间为):