Easy草稿★★★★★O(n) 時間 · O(1) 空間
Bit ManipulationDynamic Programming
Patterns💡 位元技巧
尚未複習過

解法已隱藏 — 先讀題目敘述、自己想想看,再點上方按鈕揭曉。

338Counting BitsBit ManipulationEasyBit Manipulation

給定整數 n,回傳長度為 n + 1 的陣列 ans,其中 ans[i]i 的二進位表示中 1 的個數。要求 O(n) 時間複雜度。

Example:

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

Intuition

TIP

核心思路:利用 DP 關係 dp[i] = dp[i >> 1] + (i & 1),右移一位的結果已經算過。

Approaches

1. Per-Number Popcount — O(n log n) / O(1)
  • Idea: 對 0 到 n 的每個數字,分別用 Brian Kernighan 演算法計算 1 的個數
  • Time: O(n log n) - 每個數字最多 log n 個 bit
  • Space: O(1) - 不含輸出陣列
class Solution {
    fun countBits(n: Int): IntArray {
        val ans = IntArray(n + 1)
        for (i in 0..n) {
            var count = 0
            var num = i
            while (num != 0) {
                num = num and (num - 1)
                count++
            }
            ans[i] = count
        }
        return ans
    }
}
⭐ 2. DP + Right Shift — O(n) / O(1)
  • Idea: 數字 i 的 1 的個數等於 i >> 1 的 1 的個數加上 i 的最低位(i & 1)
  • Time: O(n) - 每個數字只需 O(1)
  • Space: O(1) - 不含輸出陣列
class Solution {
    fun countBits(n: Int): IntArray {
        val ans = IntArray(n + 1)
        for (i in 1..n) {
            ans[i] = ans[i shr 1] + (i and 1)
        }
        return ans
    }
}
Note: DP + Clear Lowest Set Bit

利用 i & (i - 1) 消除最低位的 1,dp[i] = dp[i & (i - 1)] + 1。

class Solution {
    fun countBits(n: Int): IntArray {
        val ans = IntArray(n + 1)
        for (i in 1..n) {
            ans[i] = ans[i and (i - 1)] + 1
        }
        return ans
    }
}

這個方法本質上是利用已計算過的結果(消除一個 1 後的數字),加上被消除的那個 1。

🔑 Takeaways