EasyBit Manipulation

Counting Bits โ€” Solution

Problem

Given a non-negative integer n, return an array of length n + 1 where each position i holds the count of 1 bits in the binary representation of i. Think of it as building a lookup table of popcount values for every integer from 0 through n.

  • Input: n = 5
  • Output: [0, 1, 1, 2, 1, 2]
  • Explanation: 5 in binary is 101 (two 1-bits), 4 is 100 (one 1-bit), 3 is 11 (two 1-bits), and so on back to 0

Intuition

The straightforward path counts bits in each number from scratch. The key insight is that every number's bit count is already encoded in a smaller number you've visited: shifting right by one drops the lowest bit, so count(i) = count(i / 2) + (is i odd?). Because we fill the table from index 0 upward, every entry we need is already computed by the time we reach i.

Approach 1 โ€” Brute Force

For each number from 0 to n, count its 1-bits by repeatedly checking the lowest bit and right-shifting until nothing remains.

  1. Initialize an empty result array
  2. For each num from 0 to n:
    • Copy num into a temporary variable so the loop variable stays intact
    • Count bits: while temp is non-zero, add the lowest bit and shift right
    • Append the count to the result
  3. Return result
1def countBits(n: int) -> list[int]:
2    result = []
3    for num in range(n + 1):
4        bit_count = 0
5        remaining = num          # copy so we don't mutate the loop variable
6        while remaining:
7            bit_count += remaining & 1   # 1 if lowest bit is set, else 0
8            remaining >>= 1              # drop the lowest bit
9        result.append(bit_count)
10    return result

Time: O(n log n) โ€” each of the n+1 numbers requires up to logโ‚‚(n) shifts to fully process.
Space: O(1) extra โ€” only the output array is used.

Approach 2 โ€” Dynamic Programming

Reuse the bit count already stored for i >> 1: any number's popcount equals the popcount of half that number plus 1 if the number is odd.

  1. Allocate bit_count of size n + 1, all zeros
  2. For each i from 1 to n:
    • bit_count[i] = bit_count[i >> 1] + (i & 1)
    • i >> 1 is i with its lowest bit dropped (already in the table); i & 1 adds that bit back if it was 1
  3. Return bit_count
1def countBits(n: int) -> list[int]:
2    bit_count = [0] * (n + 1)
3    for i in range(1, n + 1):
4        bit_count[i] = bit_count[i >> 1] + (i & 1)  # half of i is already in the table
5    return bit_count

Time: O(n) โ€” exactly one constant-time operation per index.
Space: O(1) extra โ€” only the output array is used, no auxiliary structures.

Complexity Summary

ApproachTimeSpaceWhen to use
Brute ForceO(n log n)O(1) extraLearning bit operations or n is trivially small
DP (bit shift)O(n)O(1) extraAlways โ€” same memory footprint, strictly faster

Common Mistakes

  • Allocating n elements instead of n+1: the output spans indices 0 through n inclusive โ€” new int[n] is one entry short and causes an out-of-bounds write at the last step.
  • Left shift instead of right: i << 1 doubles i and jumps past the computed table; the recurrence needs i >> 1 to look up the answer for i / 2.
  • Mutating the loop variable in brute force: writing while num: num >>= 1 overwrites the index โ€” always copy into a temporary like remaining = num before the inner loop.
  • Wrong bit selector: i & 1 checks whether the lowest bit is set (i.e., whether i is odd); using i & 2 instead checks the second bit, producing wrong counts for every odd number.
  • Misplacing dp[0]: the base case bit_count[0] = 0 is automatically correct in every language's default initialization โ€” manually setting it to 1 breaks every even number built on top of it.

Related Problems

  • number-of-1-bits โ€” counts popcount for a single integer, the building block that this problem reuses across a range
  • single-number โ€” uses XOR properties to isolate an unpaired element, another classic bit identity
  • missing-number โ€” XOR or math trick to find the gap in a sequence, same bit-manipulation mindset
  • reverse-bits โ€” mirrors individual bits of a 32-bit integer, requiring the same shift-and-mask intuition
  • bitwise-and-of-numbers-range โ€” finds the common bit prefix of a range, combining similar shift-based reasoning at the range level

Ready to practice? Try it on SkillFlow

Adaptive problems, AI follow-up interviews, and a skill score that shows exactly where you need to improve.

Practice This Problem โ†’