給定非負整數 num,對每個 0 ≤ i ≤ num 計算其二進制表示中 1 的個數,返回數組。要求 O(n) 時間、O(n) 空間,不使用內建函數 (如 __builtin_popcount)。
觀察 0~15 的結果:
i : 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 bits: 0 1 1 2 1 2 2 3 1 2 2 3 2 3 3 4
關鍵洞察:
[1] = [0] + 1[2,3] = [0,1] + 1[4,5,6,7] = [0,1,2,3] + 1[8...15] = [0...7] + 1通式:dp[i] = dp[i - highestPowerOf2] + 1,其中 highestPowerOf2 是 ≤ i 的最大 2 的冪次。
class Solution {
public:
vector countBits(int num) {
vector dp(num + 1, 0);
int offset = 1; // 當前 2 的冪次
for (int i = 1; i <= num; ++i) {
if (offset * 2 == i) offset = i; // 遇到新的 2 的冪次
dp[i] = dp[i - offset] + 1;
}
return dp;
}
};
// 奇數:最低位為 1 → dp[i] = dp[i>>1] + 1
// 偶數:最低位為 0 → dp[i] = dp[i>>1]
for (int i = 1; i <= num; ++i) {
dp[i] = dp[i >> 1] + (i & 1);
}
更簡潔,同樣 O(n),利用右移丟棄最低位的性質。
| 方法 | 時間 | 空間 | 備註 |
|---|---|---|---|
| 暴力逐位統計 | O(n log n) | O(1) | 每數最多 32 位 |
| Brian Kernighan 算法 | O(n * k) | O(1) | k 為 1 的個數 |
| DP (最高冪次) | O(n) | O(n) | 單次遍歷,無分支預測失敗 |
| DP (奇偶性) | O(n) | O(n) | 代碼最短,位運算極快 |
本題核心在於發現重疊子問題與最優子結構:
面試時優先寫奇偶性版本:代碼極短、邏輯清晰、性能最優。