Loading

[LeetCode] 338. Counting Bits(数比特位)

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

  1. You should make use of what you have produced already.
    你应该充分利用你已经生成的数据。

  2. 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] 等等。依之前的规律生成新的区间。

  3. 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{countBit}(x) = \begin{cases} \texttt{countBit}(\texttt{shiftRight}(x, 1)) & x 为偶数 \\ \texttt{countBit}(\texttt{shiftRight}(x, 1)) + 1 & x 为奇数 \end{cases} \]

其中 \(\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
    }
}
posted @ 2020-10-24 09:34  Zhongju.copy()  阅读(100)  评论(0编辑  收藏  举报