题目描述
给定一个非负整数 $num$,对于所有的 $i$,$0 \le i \le num$,计算出 $i$ 的二进制表示中1的个数。
进一步:
- 计算量是 $O(nlogn)$ 的算法很简单,你能否想出计算量是 $O(n)$ 的算法?
- 空间复杂度只能是 $O(n)$;
- 不可以使用C++中的
__builtin_popcount
等内建函数;
样例
输入:5
输出:[0,1,1,2,1,2]。
算法
(动态规划) $O(n)$
令f[i]
表示 $i$ 的二进制表示中1的个数。
则f[i]
可以由f[i/2]
转移过来,$i$ 的二进制表示和 $\lfloor i/2 \rfloor$ 的二进制表示除了最后一位都一样,所以f[i] = f[i/2] + (i&1)
;
时间复杂度分析:总共有 $n$ 个状态,每个状态进行转移的计算量是 $O(1)$,所以总时间复杂度是 $O(n)$。
C++ 代码
class Solution {
public:
vector<int> countBits(int num) {
vector<int> f(num + 1, 0);
for (int i = 1; i <= num; i ++ )
f[i] = f[i >> 1] + (i & 1);
return f;
}
};
因为这个数是由前面的数得来的,所以用lowbit来获取前面的数也是可以的
这个也可以AC的
不知道lowbit的计算需要多长的时间复杂度,有没有大佬给我说一下谢谢
%%%
请问为啥 (i & 1)这里一定要加括号呢?
f[i] = f[i & (i - 1)] + 1
也是可以滴有道理呀,很棒!
诸神,请收下我的膝盖。
谦虚了23333
求教一下为什么呢,不是很理解,谢谢
当 i = 1 时, f[1] = f[0] + 1; 那f[0] 是从哪里来的呢
vector<int> f(num + 1, 0)
这条语句会将所有f[i]
初始化成0,包括f[0]
。