LeetCode 338. Counting Bits
问题描述
Given a non negative integer number num. For every numbers i in the range 0 ≤ i ≤ num calculate the number of 1’s in their binary representation and return them as an array.
Example
Example 1:
Input: 2
Output: [0,1,1]
Example 2:
Input: 5
Output: [0,1,1,2,1,2]
Follow up
- It is very easy to come up with a solution with run time O(n*sizeof(integer)). But can you do it in linear time O(n) /possibly in a single pass?
- Space complexity should be O(n).
- Can you do it like a boss? Do it without using any builtin function like __builtin_popcount in c++ or in any other language.
Hint
- You should make use of what you have produced already.
- Divide the numbers in ranges like [2-3], [4-7], [8-15] and so on. And try to generate new range from previous.
- Or does the odd/even status of the number help you in calculating the number of 1s?
题解
简单粗暴的咱不讲,不如根据题目的提示,探究一下这些二进制数字的规律吧:
val bin 1s index
-------------------------------
0 0 0 -
-------------------------------
1 1 1 2^0
-------------------------------
2 10 1 2^1
3 11 2
-------------------------------
4 100 1 2^2
5 101 2
6 110 2
7 111 3
-------------------------------
8 1000 1 2^3
9 1001 2
10 1010 2
11 1011 3
12 1100 2
13 1101 3
14 1110 3
15 1111 4
-------------------------------
...
根据提示2,我们可以将数字按[2,3]、[4,7]、[8,15]这样分组,其依据为二进制表示所需最少位数,因为0和1比较特别,所以我们可以通过硬编码的方法来写入返回数组。观察每个分组我们可以发现,某分组结果的前一半跟上一组完全相同,而后一半由前一半加1可得,这里我们不去探究其原理,我们只将它作为一个数字游戏:已知result[0] = 0
、result[1] = 1
,利用前面的规则依次求result[2:3]
、result[4:7]
… 即每次求解一个分组,第k个分组(从1开始)的大小为2^(k+1) - 2^k
,开始下标为2^k
,每次通过将上一个分组复制到本分组的前半部分,然后将前半部分分别加1后复制给后半部分,只不过给定 num 不一定是2的幂,所以需要判断一下是否超过num,代码如下。
Code
class Solution {
public:
vector<int> countBits(int num) {
vector<int> result(num + 1, 0);
if (num > 0) result[1] = 1;
for (int k = 1; pow(2, k) <= num; ++k) {
int mid = (int)(pow(2, k + 1) - pow(2, k)) / 2;
for (int i = pow(2, k); i < pow(2, k) + mid; ++i) {
if (i > num) break;
result[i] = result[i - mid];
if (i + mid <= num) result[i + mid] = result[i] + 1;
}
}
return result;
}
};
复杂度
复杂度是一目了然的,空间复杂度为O(n),因为我们只在返回数组上操作;时间复杂度也是O(n),因为我们从头到尾只是一次给数组每个位置赋值,且每个赋值操作时间复杂度都为 O(1)。