今天的每日一题很有意思
题目
给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。
进阶
- 给出时间复杂度为 O(n*sizeof(integer)) 的解答非常容易。但你可以在线性时间O(n)内用一趟扫描做到吗?
- 要求算法的空间复杂度为O(n)。
- 你能进一步完善解法吗?要求在C++或任何其他语言中不使用任何内置函数(如 C++ 中的
__builtin_popcount
)来执行此操作。
题解
这道题看起来很简单,针对每个数都算一遍就行了
但实际上题目要求复杂度是 O(n),虽然是软要求,但是还是要想想嘛
一开始的思路是找规律加打表,但是后来感觉太笨了(虽然时间复杂度应该是满足的),官方题解给了几种方法,这里节选我喜欢的:
动态规划——最高有效位
利用去掉最高位的 1 和剩下的(已知的)相加,即可得到当前的 1 的位数。
class Solution {
public:
vector<int> countBits(int num) {
vector<int> bits(num + 1);
int highBit = 0;
for (int i = 1; i <= num; i++) {
if ((i & (i - 1)) == 0) {
highBit = i;
}
bits[i] = bits[i - highBit] + 1;
}
return bits;
}
};
动态规划——最低有效位
class Solution {
public:
vector<int> countBits(int num) {
vector<int> bits(num + 1);
for (int i = 1; i <= num; i++) {
bits[i] = bits[i >> 1] + (i & 1);
}
return bits;
}
};
:::info 题解中还提到了 最低设置位 的解法,但是没太细想,有空可以了解一下 :::