Problem
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.
Examples
Example 1:
| |
Example 2:
| |
Solution
Video explanation
Here is the video explaining below methods in detail. Please check it out:
Method 1 - Naive
Here is the naive approach:
- Loop through all integers from
0ton. - For each integer, count the number of 1’s in its binary representation using repeated division by 2 (to extract each bit).
- Store the count for the integer in the result array.
Code
| |
| |
Complexity
- ⏰ Time complexity:
O(n log n), log N time to findcountOnesas we divide number logN times and we have to do it for n numbers. - 🧺 Space complexity:
O(1)
Method 2 - DP following power of 2
This method focuses on leveraging the properties of powers of 2. Every power of 2 (2^m) has exactly one set bit in its binary representation at mth position. Non-power-of-2 numbers can be expressed as:
| |
where r is the remainder. By observing this, we can relate the set bits of these numbers to their remainder.
Now, suppose n = 10. For that lets look at the numbers from 1 to 10:
| Number | Binary Representation | Number of Set bits |
|---|---|---|
| 0 | 0 | 0 |
| 1 | 1 | 1 |
| 2 | 10 | 1 |
| 3 | 11 | 2 |
| 4 | 100 | 1 |
| 5 | 101 | 2 |
| 6 | 110 | 2 |
| 7 | 111 | 3 |
| 8 | 1000 | 1 |
| 9 | 1001 | 2 |
| 10 | 1010 | 2 |
From the table, we can see the pattern emerge for calculating the count of set bits (dp[i]) for each number.
So, dp[0] = 0 is the base case.
Here’s how the recurrence relation applies:
dp[0] = 0(base case)dp[1] = dp[0] + 1 ⇨ 1 + dp[i - 1]dp[2] = dp[0] + 1 ⇨ 1 + dp[i - 2]dp[3] = dp[1] + 1 ⇨ 1 + dp[i - 2]dp[4] = dp[0] + 1 ⇨ 1 + dp[i - 4]dp[5] = dp[1] + 1 ⇨ 1 + dp[i - 4]dp[6] = dp[2] + 1 ⇨ 1 + dp[i - 4]dp[7] = dp[3] + 1 ⇨ 1 + dp[i - 4]dp[8] = dp[0] + 1 ⇨ 1 + dp[i - 8]dp[9] = dp[1] + 1 ⇨ 1 + dp[i - 8]dp[10] = dp[2] + 1 ⇨ 1 + dp[i - 8]
The offset corresponds to the nearest power of 2 for each number. As soon as we reach the next power of 2 (2, 4, 8, ...), we update the offset to that power of 2. This avoids recalculating results unnecessarily.
Using this insight, we can iterate through numbers from 1 to n (starting with the base case dp[0] = 0) and compute the count of set bits for each number using this recurrence formula:
$$ dp[i] = dp[i - offset] + 1 $$
Where offset is the largest power of 2 less than or equal to i.
Approach
- Use dynamic programming to compute set bits iteratively by tracking the
offset, which is the largest power of 2 less than or equal to the current number. - For each number
i, use the recurrence relation: $$ dp[i] = dp[i - offset] + 1 $$ Here,offsetcorresponds to2^m, the largest power of 2 encountered so far. - Incrementally compute results by reusing those from smaller subproblems (numbers closer to powers of 2).
Code
| |
| |
Complexity
- ⏰ Time complexity:
O(n). The algorithm iterates through numbers from1ton, performing a constant-time calculation for each number based on the recurrence relation. Thus, the time complexity isO(n). - 🧺 Space complexity:
O(n). The algorithm uses an arraydpof sizen + 1to store the set bit counts for each number. Hence, the space complexity isO(n).
Method 3 - Using DP based on division by 2
We can refine the solution further by focusing solely on how the number x changes after dividing the number by 2 which is same as right shifting the number by 1.
Lets take 2 numbers x and y where x / 2 = y. Now, there are 2 main cases.
- Case 1:
xis Even- When
xis even, its least significant bit (LSB) is0. - Dividing
xby 2 (or right-shifting it) givesy, which has the same number of set bits asx. - For example:
x = 6 (110)andy = 3 (011)6 / 2 = 3- Both
6and3have the same number of set bits (2 set bits in this case).
- When
- Case 2:
xis Odd- When
xis odd, its least significant bit (LSB) is1. - Dividing
xby 2 (or right-shifting it) givesy, and the number of set bits inxis1more than iny. - For example:
x = 7 (111)andy = 3 (011)7 / 2 = 3Number of set bits in 7 = 3, whileNumber of set bits in 3 = 2.
- When
From these cases, we can define the recurrence relation as:
$$ dp[i] = dp[i/2] + i \mod 2 $$
This uses simple arithmetic to determine whether the least significant bit contributes an additional 1.
Code
| |
| |
Complexity
- ⏰ Time complexity:
O(n). The loop runs from1ton, and each computation(bitwise AND and right shift) takes constant time (O(1)). - 🧺 Space complexity:
O(n). We maintain an array to store the results for all numbers up ton.
Method 4 - Using DP based on division by 2 but fully bitwise
Alternatively, as shown in the Number of 1 Bits problem, we can express this with binary operators for efficiency:
$$ ans[i] = ans[i \gg 1] + (i & 1) $$
i >> 1: This shifts the numberito the right by 1 bit (essentially dividingiby 2), leveraging previously computed results.(i & 1): Checks if the last bit ofiis 1 (which checks whetheriis odd).
So,
- If
iis even: There is no additional1in its binary representation compared toi >> 1. - If
iis odd: There is exactly one additional1compared toi >> 1.
Approach
dp[0] = 0(base case, 0 has no set bits).- For each number
ifrom 1 ton, computedp[i]using the transition function above.
Code
| |
| |
Complexity
- ⏰ Time complexity:
O(n). The loop runs forncomputations, and each computation takes constant time, hence it isO(n). - 🧺 Space complexity:
O(n)used bydparray.