Skip to content

4.30.3 — Counting Bits

LeetCode 338 · Easy · ★ Blind 75

The problem

Return an array where entry i is the number of set bits in i, for every i from 0 to n.

n = 5   →  [0,1,1,2,1,2]
    0 = 000 → 0
    1 = 001 → 1
    2 = 010 → 1
    3 = 011 → 2
    4 = 100 → 1
    5 = 101 → 2

The follow-up asks for one pass without calling a built-in popcount.

The pattern

Calling 4.30.2 for every number is O(n \log n). The linear solution comes from noticing that the answer for i is already in the array.

i >> 1 is i with its last bit dropped. So i has the same bits as i >> 1, plus possibly one more — the bit that was dropped.

\text{bits}[i] = \text{bits}[i \gg 1] + (i \,\&\, 1)

i >> 1 is always smaller than i, so its answer is already computed. That is a DP recurrence with an obvious fill order, and it is one line.

Check it: 5 = 101. 5 >> 1 = 2 = 010, which has 1 bit. 5 & 1 = 1. So 1 + 1 = 2 ✓.

The solution

python
class Solution:
    def countBits(self, n: int) -> List[int]:
        bits = [0] * (n + 1)

        for i in range(1, n + 1):
            bits[i] = bits[i >> 1] + (i & 1)      # drop the last bit, add it back

        return bits
ts
function countBits(n: number): number[] {
  const bits = new Array(n + 1).fill(0);

  for (let i = 1; i <= n; i++) {
    bits[i] = bits[i >> 1] + (i & 1);
  }

  return bits;
}

i >> 1 is integer division by 2, and it is exactly "remove the last binary digit".

i & 1 is the last bit — 1 if odd, 0 if even.

bits[0] = 0 comes free from the initialisation, and the loop starts at 1 so nothing reads a negative index.

The other recurrence

There is a second one-liner, using the idiom from 4.30.2:

\text{bits}[i] = \text{bits}[i \,\&\, (i-1)] + 1

i & (i-1) is i with its lowest set bit cleared — so it has exactly one fewer set bit, and it is smaller, so its answer is ready.

Both are O(n) and both are one line. The i >> 1 version is easier to explain, because "drop the last digit" is a sentence anyone follows.

A third exists, based on powers of two: for i between 2^k and 2^{k+1}, bits[i] = bits[i - 2^k] + 1. It needs an extra variable tracking the current power, so it is the least clean of the three.

Knowing that all three exist and picking the clearest is the answer to this problem.

Complexity

O(n) time, O(n) space for the output — which is required, since the output has n + 1 entries.

Against the naive O(n \log n): for n = 100,000 that is 100,000 operations instead of about 1.7 million.

Why this is really a DP problem

It is filed under bit manipulation, but the method is 4.23's:

  • State: bits[i] = the number of set bits in i.
  • Recurrence: built from a strictly smaller subproblem.
  • Base case: bits[0] = 0.
  • Fill order: ascending, because i >> 1 < i.

The bit trick is only how you find the subproblem. Recognising that a bitwise operation produces a smaller instance of the same question is the transferable move, and it is exactly what bitmask DP does at a larger scale (4.29).

Where this goes next

  • Number of 1 Bits — the single-number version. 4.30.2.
  • Bitmask DP — where the population count of a mask is part of the state, and precomputing it for every mask uses exactly this loop.
  • Gray code — consecutive values differing by one bit, built with a related recurrence.

What the interviewer will push on

"Derive the recurrence." i >> 1 drops the last bit, so add it back with i & 1. Check it on 5.

"Why is the fill order safe?" i >> 1 is strictly smaller.

"Is there another recurrence?" bits[i & (i-1)] + 1. Say why you preferred the other.

"Why is this O(n) and not O(n \log n)?" Each entry is one lookup and one addition, not a full bit scan.

"Is this bit manipulation or DP?" Both — DP with a bitwise operation finding the subproblem. Saying that shows you see the structure.

One thing to volunteer: name it as DP explicitly. The problem sits in the bit chapter, and pointing out that the method comes from elsewhere is the kind of cross-connection that makes patterns transfer.

Next: 4.30.4 Reverse Bits — a loop that reads from one end and writes to the other.