Counting Bits (Leetcode #338)
Leetcode's "Counting Bits" problem is a fascinating challenge that tests our understanding of binary numbers and efficient problem-solving. In this blog, we will walk through the problem, explore a common brute-force approach, and then provide you with a hint to solve it efficiently. We'll conclude by explaining an optimal solution step-by-step and analyzing its time and space complexity.
Understanding the Problem Statement
In the "Counting Bits" problem (Leetcode 338), we are given a non-negative integer n
, and the task is to return an array ans
of length n + 1
, where ans[i]
is the number of 1
bits in the binary representation of the number i
. Essentially, you need to count the number of 1
s (also known as set bits) for each number from 0
to n
and return all those counts in an array.
For example:
Input:
n = 5
Output:
[0, 1, 1, 2, 1, 2]
Explanation: The binary representations are
0 -> 0
,1 -> 1
,2 -> 10
,3 -> 11
,4 -> 100
,5 -> 101
. Hence, the counts of set bits are0, 1, 1, 2, 1, 2
respectively.
Brute Force Approach
A straightforward way to solve this problem is to iterate through each number from 0
to n
and count the number of 1
s in its binary representation. This can be done by repeatedly shifting the bits of each number and counting how many bits are set. Here is a brief explanation of the brute-force approach:
Create an empty list to store the counts.
Loop through each number from
0
ton
.For each number, use bitwise operations to count the
1
bits.Append the count to the result list.
However, the brute force approach is not efficient for larger values of n
, as it requires a significant amount of computation for each number, leading to a higher time complexity.
Hint to Solve the Problem Efficiently
To solve the problem efficiently, we can leverage the properties of binary representation. Notice that if i
is even, its set bits are equal to the set bits of i // 2
. If i
is odd, its set bits are one more than the set bits of i // 2
. This observation allows us to compute the number of set bits in constant time for each number based on the previously computed values.
Efficient Solution
The provided code takes advantage of the above observation to efficiently solve the problem. Here is the code and an explanation of how it works:
class Solution:
def countBits(self, n: int) -> List[int]:
# Initialize a list to store the result
result = [0] * (n + 1)
# If n is 0, simply return the result list (which is [0])
if n == 0:
return result
# The number of 1's in the binary representation of 1 is 1
result[1] = 1
# Loop through numbers from 2 to n
for i in range(2, n + 1):
if i % 2 == 0:
result[i] = result[i // 2]
else:
result[i] = result[i // 2] + 1
return result
Explanation:
Initialization: We initialize a list
result
of sizen + 1
with all values set to0
.Base Cases: If
n
is0
, we directly return[0]
. We also setresult[1] = 1
since1
has one set bit.Loop through
2
ton
: For each numberi
, we determine the number of set bits based on whetheri
is even or odd:If
i
is even,result[i] = result[i // 2]
.If
i
is odd,result[i] = result[i // 2] + 1
.
This approach is efficient because it calculates each value in constant time using previously computed values, resulting in an optimal solution.
Time and Space Complexity
Time Complexity: The time complexity of this solution is
O(n)
since we iterate through all numbers from0
ton
, and each computation takes constant time.Space Complexity: The space complexity is
O(n)
due to the additional space needed to store the result array of sizen + 1
.
Conclusion
The "Counting Bits" problem is a great way to understand the efficiency of dynamic programming and the power of using previously computed results to optimize the solution. By recognizing patterns in the binary representation of numbers, we are able to achieve an efficient O(n)
solution instead of the costly brute-force approach. This problem also emphasizes the importance of recognizing subproblems and utilizing existing results for more efficient computation.