最近上算法课上的有点心塞,所以感觉应该刷刷题来见见世面了,所以选择了LeetCode来做一点题目。
LeetCode之338:
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, 计算i在 0 <= i <= num 之间的所有数字二进制表示形式中1的数量,以数组的形式返回结果(时间复杂度要求在O(n),空间复杂度要求在O(n))
思路:
拿到题目自然而然能够想到第一个思路:可以通过位运算找出每一个数字中1的个数,然后存入一个vector中,但是这样做的话每个数字的循环次数最坏是32次,这样的话可能会超时,但是可以达到目的。在此基础上再加以改进可以得到第二个思路:每个数字转化为二进制之后都是在n – 1的基础之后上加1,所以可以让n & (n – 1)消去n中的1,直至所有的1消失。这样每个数字的移位次数只有1的个数次。虽然没有达到O(n)的时间复杂度,但是也接近了一些。抱着试试看的态度提交了一下。好吧。。。。。过了。
代码:
#include <iostream>
#include <cstdlib>
#include <vector>
class Solution {
public:
std::vector<int> countBits(int num) {
int count, tmp;
for (int i = 0; i <= num; ++i) {
for (count = 0, tmp = i; tmp; ++count) {
tmp = tmp & (tmp - 1);
}
v.push_back(count);
}
return v;
}
private:
std::vector<int> v;
};
虽然提交过了但是心里总有一些不安的感觉,因为题目要求是O(n)的复杂度,经过看提供的答案代码看到了一个很棒的解法:
代码:
vector<int> countBits(int num) {
vector<int> rst(num + 1);
rst[0] = 0;
for (int i = 1; i <= num; i++)
rst[i] = rst[i >> 1] + (i & 1);
return rst;
}
这次是真的O(n)的时间复杂度,分析代码可以总结出原理:如果n是偶数则1的个数等于n/2的中1的个数,因为n是由n/2左移一位得到的,而左移不会增加1的个数,如果n是奇数则1的个数等于n/2中1的个数加一,因为n是由n/2左移一位加一。为了代码的简洁性就写成了如上形式。不得不说这个方法太巧了!良辰服辣~~