今天的每日一题很有意思

题目

给定一个非负整数 num。对于 0 ≤ i ≤ num 范围中的每个数字 i ,计算其二进制数中的 1 的数目并将它们作为数组返回。

进阶

  • 给出时间复杂度为 O(n*sizeof(integer)) 的解答非常容易。但你可以在线性时间O(n)内用一趟扫描做到吗?
  • 要求算法的空间复杂度为O(n)。
  • 你能进一步完善解法吗?要求在C++或任何其他语言中不使用任何内置函数(如 C++ 中的 __builtin_popcount)来执行此操作。

题解

这道题看起来很简单,针对每个数都算一遍就行了
但实际上题目要求复杂度是 O(n),虽然是软要求,但是还是要想想嘛
一开始的思路是找规律加打表,但是后来感觉太笨了(虽然时间复杂度应该是满足的),官方题解给了几种方法,这里节选我喜欢的:

动态规划——最高有效位

利用去掉最高位的 1 和剩下的(已知的)相加,即可得到当前的 1 的位数。

  1. class Solution {
  2. public:
  3. vector<int> countBits(int num) {
  4. vector<int> bits(num + 1);
  5. int highBit = 0;
  6. for (int i = 1; i <= num; i++) {
  7. if ((i & (i - 1)) == 0) {
  8. highBit = i;
  9. }
  10. bits[i] = bits[i - highBit] + 1;
  11. }
  12. return bits;
  13. }
  14. };

动态规划——最低有效位

  1. class Solution {
  2. public:
  3. vector<int> countBits(int num) {
  4. vector<int> bits(num + 1);
  5. for (int i = 1; i <= num; i++) {
  6. bits[i] = bits[i >> 1] + (i & 1);
  7. }
  8. return bits;
  9. }
  10. };

:::info 题解中还提到了 最低设置位 的解法,但是没太细想,有空可以了解一下 :::