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:
For num = 5
you should return [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.
这一题意思是,给定一个数字num,求从0~num的二进制数中,分别含1的个数。如 7 = 111b,1的个数为3.
我们当然可以很容易在O(n*sizeof(integer))时间求出,只要对于每个数我们都单独求其1的个数。
可以用__builtin_popcount (int)函数直接求,或者自己写一个函数来求(令所求数为k):
int x = 1;
count = 0;
for (int i = 0; i < 32; i++){
count += x & k;
k = k >> 1;
}
return count;
因为整数有32位,故用32次迭代。
有没有办法在O(n)时间里求呢?当然有。使用动态规划的方法。
下面是一个例子
当n = 7时,vector如下:
0 1 1 2 1 2 2 3
我们按照如下划分开
1 | 1 2 | 1 2 2 3 |
分成3个区间。下一个区间大小为上一个区间的两倍。
我们定第一个区间标号为1,第二个区间标号为2。。。
2.0是第2个区间下标为0的数字,如上为1,3.2是第3个区间下标为2的数字,如上为2
我们容易发现:
3.0 = 2.0
3.1 = 2.1
3.2 = 2.0+1
3.3 = 2.1+2
同样,在第四个区间中,仍有
4.0 = 3.0 4.1 = 3.1 4.2 = 3.2 4.3 = 3.3
4.4 = 3.0+1 4.5 = 3.1+1 4.6 = 3.2+1 4.7 = 3.3+1
这个规律很容易在二进制数上进行解释,在这就不多说。
故我们得到如下规律
if (b < 上个区间大小)
f(a, b) = f(a-1, b)
else
f(a, b) = 1+ f(a-1, b % 上个区间大小)
我的代码如下:
vector<int> countBits(int num) {
vector<int> res(num+1);
if (num >= 0) res[0] = 0; //初始化最初的区间
if (num >= 1) res[1] = 1;
int base = 1; //第一个区间的起始位置
int offset = 0; //偏移量,f(a,b)中的b
int fore_size = 1; //上一个区间大小
int size = 2; //本区间大小
for (int i = 2; i <= num; i++){
if (offset < fore_size) //b < 上个区间大小
res[i] = res[base + offset++];
else if (offset < size){ //b >= 上个区间大小
res[i] = 1 + res[base+(offset%fore_size)];
offset++;
}
else{
base *= 2; //更换区间,前进一步
offset = 0; //偏移量重置
fore_size = size; //区间大小更新
size *= 2;
i--;
}
}
return res;
}
vector<int> res(num+1);
if (num >= 0) res[0] = 0; //初始化最初的区间
if (num >= 1) res[1] = 1;
int base = 1; //第一个区间的起始位置
int offset = 0; //偏移量,f(a,b)中的b
int fore_size = 1; //上一个区间大小
int size = 2; //本区间大小
for (int i = 2; i <= num; i++){
if (offset < fore_size) //b < 上个区间大小
res[i] = res[base + offset++];
else if (offset < size){ //b >= 上个区间大小
res[i] = 1 + res[base+(offset%fore_size)];
offset++;
}
else{
base *= 2; //更换区间,前进一步
offset = 0; //偏移量重置
fore_size = size; //区间大小更新
size *= 2;
i--;
}
}
return res;
}