[LeetCode] 338. Counting Bits(数比特位)
-
Difficulty: Medium
-
Related Topics: Dynamic Programming, Bit Manipulation
Description
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.
给定一个非负整数 num。对于从 0 到 num(端点包含)的每一个数 i,计算 i 的二进制表示里有多少个 1,然后以数组形式返回。
Examples
Example 1
Input: 2
Output: [0,1,1]
Example 2
Input: 5
Output: [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 a linear time O(n) /possibly in a single pass?
很容易就能想出时间复杂度为 O(n * sizeof(integer)) 的解法。但是你能在 O(n) 时间复杂度(或者说,一次遍历)解决它吗? -
Space complexity should be O(n).
空间复杂度应该是 O(n)。 -
Can you do it like a boss? Do it without using any bulitin function like __builtin_popcount in C++ or in any other language.
你能像一个 boss 那样解答吗?不使用任何内置函数(例如 C++ 里的__builtin_popcount
或者其它任何语言里的类似函数)解答。
Hints
-
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.
将数列分成若干区间,例如 [2-3],[4-7],[8-15] 等等。依之前的规律生成新的区间。 -
Or does the odd/even status of the number help you in calculating the number of 1s?
或者说,这个数的奇偶性能否帮助你计算这个数的二进制表示中 1 的数量呢?
Solution
正如提示所说,O(n * sizeof(integer)) 时间复杂度的做法很容易就能想出来。但是这题的标签里竟然有动态规划,这是啥情况?提示里说让我们找规律,那就先把 0~15 内的数据都列出来,如下表格所示:
i |
binary representation of i |
number of 1`s |
---|---|---|
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 |
你说看不出有啥规律?那我从里面单独拿出一个奇数和一个偶数的例子:
-
9 的二进制表示是
1001
,有 2 个 1;4 的二进制表示为0100
,有 1 个 1。 -
12 的二进制表示是
1100
,有 2 个 1;6 的二进制表示为0110
,有 2 个 1。
看出来了吗?一个数 num
的 1 的数量,是由其本身右移一位后的数的 1 的数量与自身奇偶性决定的,对于一个数的二进制 1 的数量 \(\texttt{countBit}(x)\) ,总有如下规律:
其中 \(\texttt{shiftRight}(x, bits)\) 表示将数 \(x\) 逻辑右移 \(bits\) 位。同时我们定义 \(\texttt{countBit}(0) = 0\) 以及 \(\texttt{countBit}(1) = 1\),这不就是天然的状态转移方程吗?于是使用动态规划法可以得到如下时间复杂度为 O(N) 的解法:
class Solution {
fun countBits(num: Int): IntArray {
if (num == 0) {
return intArrayOf(0)
}
if (num == 1) {
return intArrayOf(0, 1)
}
val result = IntArray(num + 1)
result[0] = 0
result[1] = 1
for (i in 2..num) {
result[i] = if (i % 2 == 0) {
result[i shr 1]
} else {
result[i shr 1] + 1
}
}
return result
}
}