Given an integer n, return an array ans of length n + 1 such that for each i (0 <= i <= n), ans[i] is the number of 1's in the binary representation of i.
Input: n = 2
Output: [0,1,1]
Explanation:
0 → 0 (binary) → 0 ones
1 → 1 (binary) → 1 one
2 → 10 (binary) → 1 one
Input: n = 5
Output: [0,1,1,2,1,2]
Explanation:
0 → 0 (binary) → 0 ones
1 → 1 (binary) → 1 one
2 → 10 (binary) → 1 one
3 → 11 (binary) → 2 ones
4 → 100 (binary) → 1 one
5 → 101 (binary) → 2 ones
0 <= n <= 10^5Simply count 1-bits for each number individually.
public int[] countBits(int n) {
int[] result = new int[n + 1];
for (int i = 0; i <= n; i++) {
result[i] = countOnes(i);
}
return result;
}
private int countOnes(int num) {
int count = 0;
while (num > 0) {
count += num & 1;
num >>= 1;
}
return count;
}
Time Complexity: O(n × log n) - For each number, we count bits Space Complexity: O(1) - Not counting output array
This works but is not optimal!
Key Insight: We can use previously computed results!
Pattern Discovery:
i Binary Ones i>>1 (i>>1) binary Last bit (i&1)
0 0 0 - - -
1 1 1 0 0 1
2 10 1 1 1 0
3 11 2 1 1 1
4 100 1 2 10 0
5 101 2 2 10 1
6 110 2 3 11 0
7 111 3 3 11 1
Observation:
i >> 1 removes the last bit (divides by 2)i & 1 gets the last bit (0 or 1)Why this works?
Example: For i = 5 (binary: 101)
5 >> 1 = 2 (binary: 10)5 & 1 = 1 (last bit)bits[5] = bits[2] + 1 = 1 + 1 = 2 ✓Alternative Pattern:
bits[i] = bits[i & (i-1)] + 1
What is i & (i-1)?
Example: For i = 6 (binary: 110)
i - 1 = 5 (binary: 101)i & (i-1) = 110 & 101 = 100 (4)bits[6] = bits[4] + 1 = 1 + 1 = 2 ✓This is Brian Kernighan's algorithm applied to DP!
Another Pattern:
bits[i] = bits[i - offset] + 1
where offset is the largest power of 2 ≤ i
Example:
For i = 5:
- Largest power of 2 ≤ 5 is 4
- bits[5] = bits[5-4] + 1 = bits[1] + 1 = 1 + 1 = 2
All DP Approaches:
Time Complexity: O(n)
Space Complexity: O(1)
class Solution {
public int[] countBits(int n) {
int[] bits = new int[n + 1];
for (int i = 1; i <= n; i++) {
// bits[i] = bits[i/2] + (i % 2)
bits[i] = bits[i >> 1] + (i & 1);
}
return bits;
}
}
def countBits(n: int) -> List[int]:
bits = [0] * (n + 1)
for i in range(1, n + 1):
bits[i] = bits[i >> 1] + (i & 1)
return bits
class Solution {
public int[] countBits(int n) {
int[] bits = new int[n + 1];
for (int i = 1; i <= n; i++) {
bits[i] = bits[i & (i - 1)] + 1;
}
return bits;
}
}
i=0: 0 → parent: - → bits[0] = 0
i=1: 1 → parent: 0 (0) → bits[1] = bits[0] + 1 = 0+1 = 1
i=2: 10 → parent: 1 (1) → bits[2] = bits[1] + 0 = 1+0 = 1
i=3: 11 → parent: 1 (1) → bits[3] = bits[1] + 1 = 1+1 = 2
i=4: 100 → parent: 2 (10) → bits[4] = bits[2] + 0 = 1+0 = 1
i=5: 101 → parent: 2 (10) → bits[5] = bits[2] + 1 = 1+1 = 2
i=6: 110 → parent: 3 (11) → bits[6] = bits[3] + 0 = 2+0 = 2
i=7: 111 → parent: 3 (11) → bits[7] = bits[3] + 1 = 2+1 = 3
i=1: 1 → 1 & 0 = 0 → bits[1] = bits[0] + 1 = 1
i=2: 10 → 10 & 1 = 0 → bits[2] = bits[0] + 1 = 1
i=3: 11 → 11 & 10 = 10 → bits[3] = bits[2] + 1 = 2
i=4: 100 → 100 & 11 = 0 → bits[4] = bits[0] + 1 = 1
i=5: 101 → 101 & 100 = 100 → bits[5] = bits[4] + 1 = 2
i=6: 110 → 110 & 101 = 100 → bits[6] = bits[4] + 1 = 2
i=7: 111 → 111 & 110 = 110 → bits[7] = bits[6] + 1 = 3
This problem demonstrates:
>> (right shift) with / (division) - they're the same for positives% instead of & for last bit - & is faster>> and &Q: Can you do better than O(n)?
Q: What if we only need bits[k] for specific k?
Q: How does i & (i-1) remove the rightmost set bit?
i-1 flips all bits after rightmost 1, so AND zeros themQ: Which DP approach is best?
i>>1) is most intuitive and commonly usedAll three patterns work because:
The relationship:
All give the same result but have different thought processes!
| Approach | Time | Space | Notes | |----------|------|-------|-------| | Brute Force | O(n log n) | O(1) | Count each number separately | | DP (i>>1) | O(n) | O(1) | Most intuitive ⭐ | | DP (i&(i-1)) | O(n) | O(1) | Brian Kernighan pattern | | DP (offset) | O(n) | O(1) | Power of 2 based |
Recommendation: Use Approach 2 (i>>1 + last bit) for clarity and efficiency.
Main Formula (Approach 2):
bits[i] = bits[i >> 1] + (i & 1)
where:
- i >> 1 is i divided by 2 (parent)
- i & 1 is the last bit (0 or 1)
Alternative Formula (Approach 3):
bits[i] = bits[i & (i-1)] + 1
where:
- i & (i-1) removes the rightmost 1-bit
i >> 1 : Right shift = divide by 2 = remove last biti & 1 : AND with 1 = get last bit (0 or 1)i & (i-1) : Remove rightmost 1-biti & -i : Get rightmost 1-biti | (i+1) : Set rightmost 0-bitUnderstanding these operations is crucial for bit manipulation problems!