title: Counting Bits
slug: counting-bits
difficulty: easy
leetcode_id: 338
leetcode_url: https://leetcode.com/problems/counting-bits/
categories:
  - arrays
  - dynamic-programming
  - math
patterns:
  - slug: dynamic-programming
    is_optimal: true

function_signature: "def count_bits(n: int) -> list[int]:"

test_cases:
  visible:
    - input: { n: 2 }
      expected: [0, 1, 1]
    - input: { n: 5 }
      expected: [0, 1, 1, 2, 1, 2]
    - input: { n: 0 }
      expected: [0]
  hidden:
    - input: { n: 1 }
      expected: [0, 1]
    - input: { n: 7 }
      expected: [0, 1, 1, 2, 1, 2, 2, 3]
    - input: { n: 10 }
      expected: [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2]
    - input: { n: 3 }
      expected: [0, 1, 1, 2]
    - input: { n: 15 }
      expected: [0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4]
    - input: { n: 4 }
      expected: [0, 1, 1, 2, 1]

description: |
  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`.

constraints: |
  - `0 <= n <= 10^5`

examples:
  - input: "n = 2"
    output: "[0, 1, 1]"
    explanation: "0 → 0 (zero 1's), 1 → 1 (one 1), 2 → 10 (one 1)"
  - input: "n = 5"
    output: "[0, 1, 1, 2, 1, 2]"
    explanation: "0 → 0, 1 → 1, 2 → 10, 3 → 11, 4 → 100, 5 → 101"

explanation:
  intuition: |
    Imagine you're counting the number of `1` bits in binary for every number from `0` to `n`. The naive approach would be to convert each number to binary and count — but there's a beautiful pattern hiding in the numbers themselves.

    Consider the relationship between a number and its half. When you divide a number by 2 (right shift), you simply remove the last bit. For example:
    - `5` in binary is `101` (two 1's)
    - `5 >> 1 = 2` in binary is `10` (one 1)

    The only difference is whether the **last bit** (least significant bit) was a `1` or `0`. So if you already know how many 1's are in `n // 2`, you just need to check if `n` is odd (has a `1` in the last position).

    This gives us the recurrence: `countBits(i) = countBits(i >> 1) + (i & 1)`

    Think of it like building up your answers: once you know the bit count for smaller numbers, you can instantly compute it for larger ones by leveraging the relationship between a number and its right-shifted version.

  approach: |
    We solve this using **Dynamic Programming** with a bit manipulation insight:

    **Step 1: Create the result array**

    - `ans`: Array of size `n + 1` initialised with zeros
    - `ans[0] = 0` since zero has no `1` bits (base case)

    &nbsp;

    **Step 2: Build up using the recurrence relation**

    - For each `i` from `1` to `n`:
    - `ans[i] = ans[i >> 1] + (i & 1)`
    - `i >> 1`: Right shift gives us the number with the last bit removed
    - `i & 1`: Checks if the current number is odd (last bit is `1`)

    &nbsp;

    **Step 3: Return the result**

    - Return `ans` containing the bit count for every number from `0` to `n`

    &nbsp;

    The key insight is that `i >> 1` is always less than `i` (for `i > 0`), so we've already computed its answer. This allows us to solve the problem in a single pass with O(1) work per number.

  common_pitfalls:
    - title: Using Built-in Popcount for Each Number
      description: |
        A common first approach is to use `bin(i).count('1')` or `__builtin_popcount(i)` for each number from `0` to `n`.

        While this works, it has **O(log i)** time per number (since each number has up to `log(i)` bits), giving overall **O(n log n)** time complexity.

        The problem specifically asks for an O(n) solution, which requires recognising the DP pattern.
      wrong_approach: "Loop with bin(i).count('1') for each i"
      correct_approach: "Use DP recurrence: ans[i] = ans[i >> 1] + (i & 1)"

    - title: Missing the Bit Shift Relationship
      description: |
        It's tempting to look for patterns like "powers of 2" or "consecutive numbers", but these don't lead to an elegant O(n) solution.

        The key insight is that `i` and `i >> 1` differ by exactly one bit operation. Every bit in `i >> 1` was already in `i` (just shifted right), and we only need to account for the rightmost bit we "lost".

        For example: `6 = 110` and `6 >> 1 = 3 = 11`. The bit counts are 2 and 2 respectively. Adding `6 & 1 = 0` gives us `2 + 0 = 2`. ✓
      wrong_approach: "Looking for complex mathematical patterns"
      correct_approach: "Recognise i >> 1 has same bits minus the LSB"

    - title: Off-by-One in Array Size
      description: |
        The problem asks for numbers `0` through `n` inclusive, which means `n + 1` total numbers.

        Creating an array of size `n` instead of `n + 1` will miss the last element or cause an index error.
      wrong_approach: "ans = [0] * n"
      correct_approach: "ans = [0] * (n + 1)"

  key_takeaways:
    - "**Bit manipulation + DP**: Combining bit operations with dynamic programming often reveals elegant solutions"
    - "**Right shift insight**: `i >> 1` removes the last bit, so `countBits(i) = countBits(i >> 1) + (i & 1)`"
    - "**O(n) vs O(n log n)**: Recognising subproblem relationships can reduce complexity from O(n log n) to O(n)"
    - "**Build from smaller to larger**: Classic DP pattern — use already-computed answers for smaller inputs"

  time_complexity: "O(n). We compute the answer for each number from `0` to `n` in constant time using the recurrence."
  space_complexity: "O(n). We store `n + 1` values in the result array (required by the problem output)."

solutions:
  - approach_name: Dynamic Programming with Bit Shift
    is_optimal: true
    code: |
      def count_bits(n: int) -> list[int]:
          # Result array: ans[i] will hold count of 1's in binary of i
          ans = [0] * (n + 1)

          for i in range(1, n + 1):
              # i >> 1 removes last bit, i & 1 checks if last bit is 1
              # Since i >> 1 < i, we've already computed ans[i >> 1]
              ans[i] = ans[i >> 1] + (i & 1)

          return ans
    explanation: |
      **Time Complexity:** O(n) — Single pass from 1 to n with O(1) work each.

      **Space Complexity:** O(n) — The output array of size n + 1.

      This solution uses the recurrence `ans[i] = ans[i >> 1] + (i & 1)`. Right-shifting removes the last bit, and `i & 1` tells us if that bit was a `1`. Since we process numbers in order, `i >> 1` is always already computed.

  - approach_name: Dynamic Programming with Last Set Bit
    is_optimal: true
    code: |
      def count_bits(n: int) -> list[int]:
          # Result array initialised with zeros
          ans = [0] * (n + 1)

          for i in range(1, n + 1):
              # i & (i - 1) removes the rightmost set bit
              # So we add 1 to the count of the number without that bit
              ans[i] = ans[i & (i - 1)] + 1

          return ans
    explanation: |
      **Time Complexity:** O(n) — Single pass with O(1) work per number.

      **Space Complexity:** O(n) — The output array.

      Alternative DP approach using `i & (i - 1)`, which clears the rightmost `1` bit. For example, `6 & 5 = 110 & 101 = 100 = 4`. Since `i & (i - 1) < i`, we've already computed its bit count, and we just add `1` for the bit we removed.

  - approach_name: Built-in Popcount
    is_optimal: false
    code: |
      def count_bits(n: int) -> list[int]:
          ans = []

          for i in range(n + 1):
              # Convert to binary string and count '1' characters
              ans.append(bin(i).count('1'))

          return ans
    explanation: |
      **Time Complexity:** O(n log n) — For each number, counting bits takes O(log i) time.

      **Space Complexity:** O(n) — The output array.

      This straightforward approach uses Python's built-in functions but doesn't achieve the O(n) time requested in the follow-up. Useful for understanding the problem but not optimal.
