LeetCode 338. Counting Bits
2020-05-28 20:12:34 # leetcode

Problem

LeetCode 338. Counting Bits

1. 题目简述

给出一个非负整数num,计算从0到num每个数字二进制表示中1的个数,并以数组形式返回。

Input: 5
Output: [0,1,1,2,1,2]

2. 算法思路

Brute Force

首先最暴力的解法肯定是每次向右移动一位,计算当前数字除以2的余数是多少,然后加和。

暴力解法(进阶版)

比如说对于32,二进制表示为“100000”,需要移动5次,计算6次,这很明显是不合理的嘛,因此就用到了一个十分巧妙的方法去掉末尾的0。

$$n = n & (n - 1)$$

以32举例,31的二进制表示是“11111”,和32做&操作后,n变为0,将从右至左第一个1给消除掉了,减少了很多不必要的运算。

Dynamic Programming

这种方法比较巧妙,我们其实可以找规律。我们假设从1找到8的二进制写法:

0 ->    0
1 ->    1
2 ->   10
3 ->   11
4 ->  100
5 ->  101
6 ->  110
7 ->  111
8 -> 1000

我们找一下规律,从4到7,不外乎就是把0、1、2、3四个数的二进制数前面加个1,多了一个1;我们可以想象,从8到15,也就是比从0到7每个数多了一个1,因此我们用一个lower变量表示比8小的每个数的1的个数,当到达下一个节点时,更新lower为1.

3. 解法

Dynamic Programming

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

class Solution {
public int[] countBits(int num) {
int pow = 1;
int lower = 1;
int[] res = new int[num + 1];
Arrays.fill(res, 0);

for (int i = 1; i <= num; i++) {
if (i == pow) {
pow = pow << 1;
res[i] = 1;
lower = 1;
} else {
res[i] = res[lower] + 1;
lower++;
}
}

return res;
}
}

暴力解法进阶版:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Solution {
public int[] countBits(int num) {
int[] result = new int[num + 1];
for (int i = 1; i <= num; i++) {
result[i] = fastCountBits(i);
}
return result;
}

private int fastCountBits(int num) {
int i;

for (i = 0; num != 0; i++) {
num = num & (num - 1);
}

return i;
}
}