LeetCode中338Counting Bits的题解

来源:互联网 发布:腾讯红包数据 编辑:程序博客网 时间:2024/06/07 05:02


题目来源是LeetCode中的338题,题干意思很简单,就是给出一个非零整数,要求计算该整数范围内的每一个数所对应的二进制位中“1”的数量,并将其存储在一个数组中,返回数组。题目在LeetCode中属于中等难度题,因为特地要求了时间复杂度和空间复杂度均为O(n),按照常规做法很容易得到所需要的输出结果,但是时空间复杂度达不到要求。这也是该题的关键所在,把这道题的解题思路整理如下。

一、原题描述以及样例输出

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].

二、思路整理及算法复杂度分析

按照常规思路分析,很容易就把num范围内每一个数的二进制表示出来,存到一个数组中,然后在每个数对应的数组中计算“1”的数量,将结果输出到一个新数组,最后返回该新数组。这样得到的结果没有问题,但时间复杂度很明显达不到要求,首先对num范围内的每一个数遍历,然后对于每一个数转换成二进制形式,这样时间复杂度为O(n*sizeof(num)),比题目要求多出很多。而且空间复杂度经过简单的分析也可得知为O(n^2)。

由于题目要求的时间复杂度为O(n),而对于n个数最少要遍历n次,而这个操作的时间复杂度就已经为O(n),因此对于每一个数去计算其“1”的数量的操作时间复杂度必须为O(1)才能满足要求。并且因为输出结果所占用的空间复杂度为O(n),那么也不能将每个数分解为二进制数组形式然后计数。也就是说,对于num范围内的每一个数,我们必须在不占用新空间的情况下使用时间复杂度为O(1)的操作去得到该数“1”的数量。

这就使得我们在得出新结果的同时,必须要利用上上一步所得到的结果,很容易想到递归的思路,但其实也没有递归那么复杂。在纸上写下几位连续整数的二进制形式,只需要仔细观察相邻两个数就会发现,这两个数在计算机中进行二进制“与”操作后,所得到结果一定小于后一位数,并且其中“1”的数量再加1则为相邻两数中后一位数中“1”的数量!这样我们在计算一个数“1”的数量时,就可以利用前面所计算的结果得到新结果,不需要声明新地址,也仅仅进行了加法的操作,对于每个数计算结果时时间复杂度上为O(1),符合题目要求。有了这个思路,接下来用代码就比较好实现了,具体的代码实现见下一节。

三、c++代码实现

根据上面的分析,我们用c++将其实现如下,其中时间复杂度和空间复杂度均为O(n)。

class Solution {public:    vector<int> countBits(int num) {        vector<int> bit(num+1,0);        int i;        for(i = 1; i <= num; i++) {            bit[i] = bit[i&(i-1)] + 1;        }        return bit;    }};

1 0