【Leetcode】338. Bit位计数

每次刷leetcode都有一种发现新大陆的感觉。

题目链接:https://leetcode-cn.com/problems/counting-bits/description/

给定一个非负整数 num。 对于范围 0 ≤ i ≤ num 中的每个数字 i ,计算其二进制数中的1的数目并将它们作为数组返回。

示例:
比如给定 num = 5 ,应该返回 [0,1,1,2,1,2].

进阶:

    • 给出时间复杂度为O(n * sizeof(integer)) 的解答非常容易。 但是你可以在线性时间O(n)内用一次遍历做到吗?
    • 要求算法的空间复杂度为O(n)。
    • 你能进一步完善解法吗? 在c ++或任何其他语言中不使用任何内置函数(如c++里的 __builtin_popcount)来执行此操作。

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

 

找规律,以分割线为界限。比如4-7。4,5和2,3相同,但是6,7却是2,3 加 1. 而8-15也符合类似的规则。可以用这个规律写下代码。

 

大神解法:

1 public int[] countBits(int num) {
2     int[] f = new int[num + 1];
3     for (int i=1; i<=num; i++) 
4         f[i] = f[i >> 1] + (i & 1);
5     return f;
6 }

下面这种方法就更加巧妙了,巧妙的利用了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值都是i&(i-1)对应的值加1,这样我们就可以写出代码如下:

 1 class Solution {
 2 public:
 3     vector<int> countBits(int num) {
 4         vector<int> res(num + 1, 0);
 5         for (int i = 1; i <= num; ++i) {
 6             res[i] = res[i & (i - 1)] + 1;
 7         }
 8         return res;
 9     }
10 };

唉,都是人才啊。怎么想到的

posted @ 2018-05-13 17:54  程序员博博  阅读(1196)  评论(0编辑  收藏  举报