Description
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
1.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?
2.Space complexity should be O(n).
3.Can you do it like a boss? Do it without using any builtin function like __builtin_popcount in c++ or in any other language.
Solution 1(C++)
class Solution{
public:
vector<int> countBits(int num){
vector<int> res(num+1, 0);
for(int i=0; i<=num; i++){
bitset<32> b(i);
res[i] = b.count();
}
return res;
}
};
Solution 2(C++)
class Solution{
public:
inline bool ispowerof2(int n){
return (n & (n-1)) ==0;
}
vector<int> countBits(int num){
vector<int> dp(num+1);
dp[0] = 0;
if(num >= 1) dp[1] = 1;
int curr = 2;
int neatest = 2;
while(curr <= num){
nearest = ispowerof2(curr) > curr : nearest;
dp[curr] = 1 + dp[curr-nearest];
curr++;
}
return dp;
}
};
Solution 3(C++)
class Solution{
public:
vector<int> res(num+1, 0);
for(int i=1; i<num+1; i++){
res[i] = res[i/2] + i%2;
}
return res;
};
Solution 4(C++)
class Solution{
public:
vector<int> countBits(int num){
vector<int> res(num+1, 0);
for(int i=1; i<num+1; i++){
res[i] = res[i&(i-1)] + 1;
}
return res;
}
};
算法分析
解法一
解法一使用了bitset这个数据容器,具体可以参考:
并不是十分取巧的方法,但是比较容易想出来。
解法二
解法二、解法三、解法四都是基于一点:观察如下数字:
^^^^
0 : 0000 -->0
1 : 0001 -->1
--------------------------
2 : 0010 -->1
3 : 0011 -->2
--------------------------
4 : 0100 -->1
5 : 0101 -->2
6 : 0110 -->2
7 : 0111 -->3
--------------------------
8 : 1000 -->1
9 : 1001 -->2
10: 1010 -->2
11: 1011 -->3
12: 1100 -->2
13: 1101 -->3
14: 1110 -->3
15: 1111 -->4
--------------------------
^^^^
罗列了这么多数字,可以根据横线:”———”的划分,发现0、·与1、2最低为对应相同,但是高一位前二者为0,后二者为1。同样·0、1、2、3与4、5、6、7只有最高位对应不同,前四者均为0,后四者均为1。后面的就不一一赘述了。所以假如说我要求某一个数的二进制的1的个数,可以转换为对应的一个数的二进制的1的个数加1。这个对应的数该怎么求?就是解法二、三、四要解决的问题。
解法二的做法是,用curr表示要求的数,而nearest表示离他最近的转换的分界线,也就是我在表格中横线:”———”的划分下一个数:是2的幂次数。比如,要求10,那么根据表格,可以很轻松的找到,10就是2的二进制的1个数,再加上1。而10-8=2。所以解法二中有一句:
nearest = ispowerof2(curr) ? curr : nearest;
就是如果curr是新的2的幂次数,那么就要更新nearest。那么状态转移方程也可求得:dp[curr] = 1 + dp[curr-nearest];
解法三
根据进一步的观察,可以换个角度来看,如果一个dp[i] = n,那么dp[i*2] = n,肯定成立,相当于将n左移一位。那么不难的到新的状态转移方程:dp[i] = dp[i/2] + i%2。
解法四
根据上面的讨论,由于0~n中的数字可以按照最高位数字不同进行转换。所以,只要把二进制最高位数字去掉,那么就可以直接找到对应的数字。所以可得到状态转移方程:dp[i] = dp[i&(i-1)] + 1。类似的题目还可以参考:
程序分析
略。