[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 的数量 ( exttt{countBit}(x)) ,总有如下规律:

[ exttt{countBit}(x) = egin{cases} exttt{countBit}( exttt{shiftRight}(x, 1)) & x 为偶数 \ exttt{countBit}( exttt{shiftRight}(x, 1)) + 1 & x 为奇数 end{cases} ]

其中 ( exttt{shiftRight}(x, bits)) 表示将数 (x) 逻辑右移 (bits) 位。同时我们定义 ( exttt{countBit}(0) = 0) 以及 ( exttt{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
    }
}
原文地址:https://www.cnblogs.com/zhongju/p/13867632.html