Counting Bits solution and explanation
9 more videos on this problem
Counting Bits - Dynamic Programming - Leetcode 338 - PythonNeetCode · 13:24 · 206K views
Counting Bits | Leetcode #338Techdose · 11:51 · 77K views
Counting Bits | Follow Up Qn | 2 Approaches | AMAZON | Leetcode - 338codestorywithMIK · 10:25 · 21K views
Leetcode 338. Counting Bits | Bit Manipulation | Brute Force to Optimal | 2 Solutions with full codeCode with Alisha · 13:57 · 11K views
Counting Bits - LeetCode 338 - Python - Dynamic Programming! (Bit Shift Solution)Deepti Talesra · 9:03 · 5K views
Leetcode | 338. Counting Bits | Java SolutionDeveloper Docs · 6:48 · 5K views
Counting Bits Problem | Detailed Explanation and Code (Java ) | LeetCode 338Prepflix · 5:47 · 2K views
Microsoft Coding Interview Question - Counting Bits - LeetCode 338AlgoJS · 6:17 · 1K views
Counting Bits LeetCode | Multiple Approaches | LeetCode 338 | Programming TutorialsProgramming Tutorials · 13:22 · 1K views
Counting Bits | LeetCode 338 (Easy)Destination FAANG · 14:38 · 1K views
The 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.
Example 1:
Input: n = 2 Output: [0,1,1] Explanation: 0 --> 0 1 --> 1 2 --> 10
Example 2:
Input: n = 5 Output: [0,1,1,2,1,2] Explanation: 0 --> 0 1 --> 1 2 --> 10 3 --> 11 4 --> 100 5 --> 101
Constraints:
0 <= n <= 105
Follow up:
- It is very easy to come up with a solution with a runtime of
O(n log n). Can you do it in linear timeO(n)and possibly in a single pass? - Can you do it without using any built-in function (i.e., like
__builtin_popcountin C++)?
Approach overview
Problem Overview: Given an integer n, return an array ans where ans[i] represents the number of 1 bits in the binary representation of i for every number from 0 to n. The challenge is computing all counts efficiently instead of recalculating bits for each number independently.
Approach 1: Naïve Count Bits Approach (O(n log n) time, O(1) extra space)
The straightforward approach processes each integer from 0 to n and counts the set bits in its binary representation. You repeatedly divide the number by two (right shift) and increment a counter whenever the least significant bit is 1. Another variation uses Brian Kernighan’s trick (x = x & (x - 1)) to remove the lowest set bit until the number becomes zero. This approach is simple and demonstrates understanding of bit manipulation, but each number may take up to O(log n) operations to process. Overall complexity becomes O(n log n). It works fine for small inputs but does redundant work because adjacent numbers share overlapping binary patterns.
Approach 2: Dynamic Programming with Bit Relation (O(n) time, O(n) space)
The optimal solution uses a recurrence relationship between numbers. Observe that shifting a number right removes its least significant bit. The count of set bits for i can be derived from a smaller number: dp[i] = dp[i >> 1] + (i & 1). The expression i >> 1 removes the last bit, while (i & 1) checks whether that removed bit was 1. Since dp[i >> 1] has already been computed earlier, you reuse previously calculated results instead of recomputing the entire bit count.
This creates a bottom‑up dynamic programming solution. Start with dp[0] = 0. Iterate from 1 to n, applying the recurrence. Each step performs constant-time bit operations, producing an overall time complexity of O(n) with O(n) space for the result array.
Another equivalent recurrence uses the expression dp[i] = dp[i & (i - 1)] + 1. The operation i & (i - 1) removes the lowest set bit from i. Since that smaller value already has its bit count stored, you simply add one. Both formulas rely on properties of bit manipulation and reuse computed results, which is the core idea behind dynamic programming.
Recommended for interviews: Interviewers expect the dynamic programming solution with O(n) time. Starting with the naïve bit counting approach shows basic understanding of binary operations. Moving to the recurrence relation demonstrates optimization skills and recognition of overlapping subproblems, which is exactly what the question is designed to test.
Approach 1: Naïve Count Bits Approach
This approach involves converting each number to its binary representation and counting the number of 1's in this binary form. We'll iterate through each number from 0 to n, convert the number to its binary form using standard built-in methods, and then count the number of 1's using another built-in method or implement a small function for counting.
def countBits(n):
return [bin(i).count('1') for i in range(n + 1)]import java.util.*;
public class Solution {
public int[] countBits(int n) {
int[] res = new int[n + 1];
for (int i = 0; i <= n; i++) {
res[i] = Integer.bitCount(i);
}
return res;
}
}#include <vector>
using namespace std;
vector<int> countBits(int n) {
vector<int> res(n + 1, 0);
for (int i = 0; i <= n; ++i) {
res[i] = __builtin_popcount(i);
}
return res;
}var countBits = function(n) {
let res = new Array(n + 1);
for (let i = 0; i <= n; i++) {
res[i] = i.toString(2).split('0').join('').length;
}
return res;
};#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int* countBits(int n, int* returnSize) {
*returnSize = n + 1;
int* res = (int*)malloc(sizeof(int) * (*returnSize));
for (int i = 0; i <= n; ++i) {
int count = 0;
int num = i;
while (num) {
count += num & 1;
num >>= 1;
}
res[i] = count;
}
return res;
}public class Solution {
public int[] CountBits(int n) {
int[] res = new int[n + 1];
for (int i = 0; i <= n; i++) {
res[i] = CountOnes(i);
}
return res;
}
private int CountOnes(int num) {
int count = 0;
while (num > 0) {
count += num & 1;
num >>= 1;
}
return count;
}
}In this Python solution, a list comprehension is used to generate the result. The bin function converts the number to a binary string and the count method counts the number of 1's.
Time Complexity: O(n log n) due to conversion and counting.
Space Complexity: O(n) for the list storage.
Approach 2: Dynamic Programming Approach
This optimized approach utilizes dynamic programming. The key observation is that for a number i, the number of 1's is equal to the number of 1's in i/2 plus i mod 2. This allows us to build the array progressively and with linear time complexity.
def countBits(n):
res = [0] * (n + 1)
for i in range(1, n + 1):
res[i] = res[i >> 1] + (i & 1)
return resimport java.util.*;
public class Solution {
public int[] countBits(int n) {
int[] res = new int[n+1];
for (int i = 1; i <= n; i++) {
res[i] = res[i >> 1] + (i & 1);
}
return res;
}
}#include <vector>
using namespace std;
vector<int> countBits(int n) {
vector<int> res(n + 1, 0);
for (int i = 1; i <= n; ++i) {
res[i] = res[i >> 1] + (i & 1);
}
return res;
}var countBits = function(n) {
let res = new Array(n + 1);
res[0] = 0;
for (let i = 1; i <= n; i++) {
res[i] = res[i >> 1] + (i & 1);
}
return res;
};#include <stdio.h>
#include <stdlib.h>
int* countBits(int n, int* returnSize) {
*returnSize = n + 1;
int* res = (int*)malloc(sizeof(int) * (*returnSize));
res[0] = 0;
for (int i = 1; i <= n; ++i) {
res[i] = res[i >> 1] + (i & 1);
}
return res;
}public class Solution {
public int[] CountBits(int n) {
int[] res = new int[n+1];
for (int i = 1; i <= n; i++) {
res[i] = res[i >> 1] + (i & 1);
}
return res;
}
}This Python solution creates a list initialized to zero and uses a loop to fill each position using the count from the bit-shifted index plus the last bit.
Time Complexity: O(n) due to loop through each number.
Space Complexity: O(n) for the array space.
Approach 3: Default Approach
function countBits(n: number): number[] {
const ans: number[] = Array(n + 1).fill(0);
for (let i = 0; i <= n; ++i) {
ans[i] = bitCount(i);
}
return ans;
}
function bitCount(n: number): number {
let count = 0;
while (n) {
n &= n - 1;
++count;
}
return count;
}func countBits(n int) []int {
ans := make([]int, n+1)
for i := 0; i <= n; i++ {
ans[i] = bits.OnesCount(uint(i))
}
return ans
}Complexity comparison
| Approach | Complexity |
|---|---|
| Naïve Count Bits Approach | Time Complexity: O(n log n) due to conversion and counting. |
| Dynamic Programming Approach | Time Complexity: O(n) due to loop through each number. |
| Default Approach | – |
Complexity analysis
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Naïve Count Bits | O(n log n) | O(1) | Good for understanding basic bit counting or when implementing a quick brute-force solution |
| Dynamic Programming with Bit Relation | O(n) | O(n) | Best choice for interviews and large inputs since each number is computed using previously calculated results |
Frequently asked questions
Is Counting Bits easy or hard?
Counting Bits is classified as an Easy problem on LeetCode. The brute-force idea is simple, but recognizing the dynamic programming relationship that reduces the complexity to O(n) is the key insight interviewers look for.
How to solve Counting Bits in O(n)?
Use a dynamic programming recurrence. For each number i, compute dp[i] = dp[i >> 1] + (i & 1). The right shift removes the last bit while (i & 1) determines whether the removed bit was 1. Since dp[i >> 1] is already known, each step takes constant time.
What is the best approach for Counting Bits?
The dynamic programming approach using the relation dp[i] = dp[i >> 1] + (i & 1) is the most efficient. It computes the number of set bits for each number from 0 to n using previously calculated results. This reduces the time complexity to O(n) with O(n) space.
What data structure is used in Counting Bits?
The solution primarily uses an array (or list) to store results for all values from 0 to n. This array acts as the dynamic programming table where each index represents the number and the stored value represents its bit count.
What is the time complexity of Counting Bits?
The optimal dynamic programming solution runs in O(n) time because each integer from 1 to n is processed exactly once with constant-time bit operations. The naïve method that counts bits individually takes O(n log n) time since each number may require up to log n bit checks.
Counting Bits Python or Java solution approach?
Both Python and Java implementations follow the same DP formula: dp[i] = dp[i >> 1] + (i & 1). Initialize an array of size n + 1, iterate from 1 to n, and fill each position using the recurrence. The logic remains identical across languages.
Is Counting Bits asked at Google, Amazon, or Meta?
Counting Bits is a common bit manipulation and dynamic programming interview problem that appears in coding interviews at large tech companies. Variations of this problem have been reported in interviews at companies like Google, Amazon, and Microsoft because it tests understanding of binary operations and DP optimization.
