Subsets Generation Using Bitmask in DSA Javascript - Time & Space Complexity
We want to understand how the time needed to generate all subsets grows as the input list gets bigger.
How does the number of steps change when we have more items to consider?
Analyze the time complexity of the following code snippet.
const subsets = (nums) => {
const result = [];
const n = nums.length;
for (let mask = 0; mask < (1 << n); mask++) {
const subset = [];
for (let i = 0; i < n; i++) {
if (mask & (1 << i)) subset.push(nums[i]);
}
result.push(subset);
}
return result;
};
This code creates all possible subsets of an array by using a bitmask to decide which elements to include.
- Primary operation: Two nested loops: outer loop runs through all bitmasks, inner loop checks each element for inclusion.
- How many times: Outer loop runs 2^n times, inner loop runs n times for each outer loop.
As the input size grows, the number of subsets doubles for each new element, and for each subset, we check all elements.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 * 2^10 = 10,240 |
| 100 | About 100 * 2^100 (a very large number) |
| 1000 | About 1000 * 2^1000 (extremely large, not practical) |
Pattern observation: The operations grow very fast, doubling with each added element and multiplying by the number of elements checked.
Time Complexity: O(n * 2^n)
This means the time needed grows very fast as the input size increases, because we generate all subsets and check each element for each subset.
[X] Wrong: "Generating subsets is just O(2^n) because there are 2^n subsets."
[OK] Correct: We also spend time checking each element for every subset, so the total work multiplies by n, making it O(n * 2^n).
Understanding this time complexity helps you explain why generating all subsets is expensive and shows you can analyze nested loops with exponential growth clearly.
"What if we only generated subsets of a fixed size k instead of all sizes? How would the time complexity change?"
Practice
Solution
Step 1: Understand bitmask representation
Each bit in a bitmask represents whether an element is included (1) or excluded (0) from a subset.Step 2: Generate subsets using bitmask
By iterating through all bitmasks from 0 to 2^n - 1, we get all possible combinations of elements, i.e., all subsets.Final Answer:
All possible subsets of the set -> Option CQuick Check:
Bitmask = subsets [OK]
- Thinking bitmask generates only largest subsets
- Confusing subsets with permutations
- Assuming bitmask filters subsets by size
Solution
Step 1: Understand bitmask range
Bitmask must cover all numbers from 0 to 2^n - 1, which is (1 << n) in JavaScript.Step 2: Correct loop syntax
The loop should start at 0 and run while mask is less than (1 << n), incrementing mask by 1 each time.Final Answer:
for (let mask = 0; mask < (1 << n); mask++) -> Option BQuick Check:
Loop from 0 to 2^n-1 [OK]
- Starting loop at 1 instead of 0
- Using mask <= n instead of mask < 2^n
- Confusing n with 2^n in loop condition
const arr = ["a", "b"];
const subsets = [];
for (let mask = 0; mask < (1 << arr.length); mask++) {
let subset = [];
for (let i = 0; i < arr.length; i++) {
if ((mask & (1 << i)) !== 0) {
subset.push(arr[i]);
}
}
subsets.push(subset);
}
console.log(subsets);Solution
Step 1: Understand bitmask iterations
For arr length 2, mask runs from 0 to 3 (0b00 to 0b11), generating 4 subsets.Step 2: Map mask bits to elements
mask=0 (0b00) -> [], mask=1 (0b01) -> ["a"], mask=2 (0b10) -> ["b"], mask=3 (0b11) -> ["a", "b"]Final Answer:
[[], ["a"], ["b"], ["a", "b"]] -> Option AQuick Check:
All subsets of ["a", "b"] listed [OK]
- Missing empty subset []
- Swapping order of subsets
- Ignoring bitmask 0 producing empty subset
const arr = [1, 2, 3];
const subsets = [];
for (let mask = 0; mask <= (1 << arr.length); mask++) {
let subset = [];
for (let i = 0; i < arr.length; i++) {
if (mask & (1 << i)) {
subset.push(arr[i]);
}
}
subsets.push(subset);
}
console.log(subsets);Solution
Step 1: Check outer loop range
The outer loop runs from 0 to (1 << arr.length) inclusive, which is one too many iterations (should be less than).Step 2: Correct loop condition
It should be mask < (1 << arr.length) to avoid an extra invalid subset iteration.Final Answer:
The outer loop condition should be mask < (1 << arr.length), not <=. -> Option DQuick Check:
Loop runs 2^n times, not 2^n+1 [OK]
- Using <= instead of < in loop condition
- Confusing bitwise AND (&) with OR (|)
- Extending inner loop beyond array length
arr = [1, 2, 3, 4], how many subsets will be generated by the bitmask method, and what is the subset generated when mask = 9? (mask is in decimal)Solution
Step 1: Calculate total subsets for array length 4
Total subsets = 2^4 = 16.Step 2: Decode mask 9 in binary and map to elements
9 in binary is 1001. Bits set at positions 0 and 3 correspond to arr[0] = 1 and arr[3] = 4.Final Answer:
16 subsets; subset for mask 9 is [1, 4] -> Option AQuick Check:
2^4 = 16 subsets; mask 9 = 1001 binary [OK]
- Miscounting total subsets as 15
- Misinterpreting binary mask bits
- Confusing element indices with bit positions
Ready to prove it?
Pass the test and this pattern moves unknown
