Subsets Generation Using Bitmask in DSA Typescript - Time & Space Complexity
We want to understand how the time needed to generate all subsets grows as the input list gets bigger.
Specifically, how does the number of steps change when we use bitmasking to create subsets?
Analyze the time complexity of the following code snippet.
function generateSubsets(nums: number[]): number[][] {
const n = nums.length;
const subsets: number[][] = [];
for (let mask = 0; mask < (1 << n); mask++) {
const subset: number[] = [];
for (let i = 0; i < n; i++) {
if ((mask & (1 << i)) !== 0) {
subset.push(nums[i]);
}
}
subsets.push(subset);
}
return subsets;
}
This code creates all possible subsets of the input array by using bitmasks to decide which elements to include.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The outer loop runs through all bitmasks from 0 to 2n - 1.
- Inner loop: For each bitmask, it checks each bit position from 0 to n-1.
- How many times: Outer loop runs 2n times, inner loop runs n times for each outer iteration.
As the input size n grows, the number of subsets doubles for each new element added.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 * 210 = 10,240 operations |
| 20 | About 20 * 220 = 20,971,520 operations |
| 30 | About 30 * 230 = 32,212,254,720 operations |
Pattern observation: The operations grow very fast, roughly doubling for each extra element, multiplied by the element count.
Time Complexity: O(n * 2n)
This means the time needed grows very fast as the input size increases, because we must check all subsets and their elements.
[X] Wrong: "Generating subsets is just O(2n) because there are 2n subsets."
[OK] Correct: Each subset can have up to n elements, and we spend time checking each element for every subset, so the total work is multiplied by 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 the bitmask represents whether an element is included (1) or excluded (0) in a subset.Step 2: Generate subsets using bitmask
By iterating through all bitmasks from 0 to 2^n - 1, we cover all combinations of elements, thus all subsets.Final Answer:
All possible subsets of the set -> Option DQuick Check:
Bitmask = All subsets [OK]
- Thinking bitmask generates only even or odd subsets
- Confusing subsets with permutations
- Assuming bitmask generates only largest subsets
arr of length n?Solution
Step 1: Understand bitmask range
Bitmask must cover all numbers from 0 to 2^n - 1, which is (1 << n) in code.Step 2: Check loop correctness
Loop from 0 to less than (1 << n) correctly iterates all bitmasks representing subsets.Final Answer:
for (let mask = 0; mask < (1 << n); mask++) {} -> Option CQuick Check:
Loop 0 to 2^n-1 = for (let mask = 0; mask < (1 << n); mask++) {} [OK]
- Using mask <= n instead of mask < 2^n
- Starting loop from 1 misses empty subset
- Using mask < n only covers single elements
const arr = ["a", "b", "c"];
const subsets: string[][] = [];
const n = arr.length;
for (let mask = 0; mask < (1 << n); mask++) {
const subset: string[] = [];
for (let i = 0; i < n; i++) {
if ((mask & (1 << i)) !== 0) {
subset.push(arr[i]);
}
}
subsets.push(subset);
}
console.log(subsets);Solution
Step 1: Understand bitmask iteration
Loop runs from 0 to 7 (2^3 - 1), generating all subsets of 3 elements.Step 2: Trace subsets generated
Each mask selects elements where bit is set, producing all subsets including empty and full set.Final Answer:
[[], ["a"], ["b"], ["a", "b"], ["c"], ["a", "c"], ["b", "c"], ["a", "b", "c"]] -> Option BQuick Check:
All subsets of 3 elements = [[], ["a"], ["b"], ["a", "b"], ["c"], ["a", "c"], ["b", "c"], ["a", "b", "c"]] [OK]
- Missing empty subset []
- Incorrect order or missing subsets
- Confusing subsets with permutations
const arr = [1, 2, 3];
const n = arr.length;
for (let mask = 0; mask <= (1 << n); mask++) {
const subset = [];
for (let i = 0; i < n; i++) {
if (mask & (1 << i)) {
subset.push(arr[i]);
}
}
console.log(subset);
}Solution
Step 1: Check outer loop range
Loop runs from 0 to (1 << n) inclusive, which is one too many iterations (should be less than).Step 2: Understand consequences
When mask equals (1 << n), it exceeds the maximum bitmask for subsets, causing an extra invalid iteration.Final Answer:
The outer loop should run while mask < (1 << n), not <= -> Option AQuick Check:
Loop limit off by one = The outer loop should run while mask < (1 << n), not <= [OK]
- Using <= instead of < in loop condition
- Changing inner loop range incorrectly
- Misusing bitwise condition checks
arr = [1, 2, 2], which code modification correctly generates all unique subsets using bitmask technique?Solution
Step 1: Understand duplicates in subsets
Bitmask generates subsets by position, so duplicates in array cause duplicate subsets.Step 2: Avoid duplicates using a Set
Storing subsets as strings in a Set removes duplicates by keeping only unique subsets.Final Answer:
Use a Set to store stringified subsets to avoid duplicates -> Option AQuick Check:
Set removes duplicate subsets [OK]
- Assuming bitmask alone removes duplicates
- Increasing bitmask range unnecessarily
- Trying to skip duplicates without sorting or tracking
Ready to prove it?
Pass the test and this pattern moves unknown
