题目:
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.
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?
题意:
给定一个非负整数num。对于每一个满足0 ≤ i ≤ num的数字i,计算其数字的二进制表示中1的个数,并以数组形式返回。
Follow up:
1、很容易想到运行时间 O(n*sizeof(integer)) 的解法。但你可以用线性时间O(n)的轮训一次算法完成吗?
2、空间复杂度应该为O(n);
3、你可以像老板那样吗?不要使用任何内建函数(比如C++的__builtin_popcount)。
Hint:
1、你应当利用已经生成的结果;
2、将数字拆分为诸如 [2-3], [4-7], [8-15] 之类的范围。并且尝试根据已经生成的范围产生新的范围;
3、数字的奇偶性可以帮助你计算1的个数吗?
转载地址:http://www.cnblogs.com/grandyang/p/5294255.html
思路一:
写出0到15之间的各个数字的二进制和1的个数如下:
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开始,2和3,是[21, 22)区间的,值为1和2。而4到7属于[22, 23)区间的,值为1,2,2,3,前半部分1和2和上一区间相同,2和3是上面的基础上每个数字加1。再看8到15,属于[23, 24)区间的,同样满足上述规律。
代码:C++版:136ms
class Solution { public: vector<int> countBits(int num) { if (num == 0) return {0}; vector<int> res{0, 1}; int k = 2, i = 2; while (i <= num) { for (i=pow(2, k-1); i<pow(2,k); ++i) { //每次2^(k-1)到2^k之间作为一次轮训 if (i > num) break; //控制结束条件 int t = (pow(2, k) - pow(2, k-1))/2; //找到一半的位置 if (i < pow(2, k-1) + t) res.push_back(res[i-t]); //前半段与之前一部分相同 else res.push_back(res[i-t] + 1); //后半段在之前的基础上加1 } ++k; } return res; } };
思路二:
如果可以使用内建函数,则可以直接调用函数bitset的count函数可以直接返回1的个数,而题目要求不允许使用内建函数。
代码:C++版:92ms
class Solution { public: vector<int> countBits(int num) { vector<int> res; for (int i=0; i<=num; ++i) { res.push_back(bitset<32>(i).count()); } return res; } };
思路三:
从以上给出的0到15数字的二进制形式以及1的个数找到另外的规律,而且规律更好,规律是,从1开始,遇到偶数时,其1的个数和该偶数除以2得到的数字的1的个数相同,遇到奇数时,其1的个数等于该奇数除以2得到的数字的1的个数再加1。
代码:C++版:96ms
class Solution { public: vector<int> countBits(int num) { vector<int> res{0}; for (int i=1; i<=num; ++i) { if (i%2 == 0) res.push_back(res[i/2]); else res.push_back(res[i/2] + 1); } return res; } };
思路四:
这种方法就更加巧妙了,巧妙的利用了i&(i - 1), 这个本来是用来判断一个数是否是2的指数的快捷方法,比如8,二进制位1000, 那么8&(8-1)为0,只要为0就是2的指数, 那么我们现在来看一下0到15的数字和其对应的i&(i - 1)值:
i bin '1' i&(i-1) 0 0000 0 ----------------------- 1 0001 1 0000 ----------------------- 2 0010 1 0000 3 0011 2 0010 ----------------------- 4 0100 1 0000 5 0101 2 0100 6 0110 2 0100 7 0111 3 0110 ----------------------- 8 1000 1 0000 9 1001 2 1000 10 1010 2 1000 11 1011 3 1010 12 1100 2 1000 13 1101 3 1100 14 1110 3 1100 15 1111 4 1110观察以上规律,可以发现每个i值处的1的个数都是i&(i-1)对应的值处的1的个数加1。
代码:C++版:84ms
class Solution { public: vector<int> countBits(int num) { vector<int> res(num+1, 0); for (int i=1; i<=num; ++i) { res[i] = res[i & (i-1)] + 1; } return res; } };