Subsets Generation Using Bitmask in DSA Rust - Time & Space Complexity
We want to understand how the time needed to generate all subsets grows as the input size increases.
How does the number of steps change when we have more elements to create subsets from?
Analyze the time complexity of the following code snippet.
fn subsets(nums: &Vec<i32>) -> Vec<Vec<i32>> {
let n = nums.len();
let mut result = Vec::new();
for mask in 0..(1 << n) {
let mut subset = Vec::new();
for i in 0..n {
if (mask & (1 << i)) != 0 {
subset.push(nums[i]);
}
}
result.push(subset);
}
result
}
This code creates all possible subsets of the input list by using bitmasks to decide which elements to include.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Two nested loops: outer loop runs through all bitmasks, inner loop checks each bit.
- 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 operations |
| 20 | About 20 * 2^20 = 20,971,520 operations |
| 30 | About 30 * 2^30 = 32,212,254,720 operations |
Pattern observation: The operations grow very fast, roughly doubling for each added element, multiplied by the input size.
Time Complexity: O(n * 2^n)
This means the time needed grows very fast as the input size increases, because we must look at every possible subset and check each element.
[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 your grasp of nested loops and exponential growth.
"What if we only collected subsets of a fixed size k instead of all sizes? How would the time complexity change?"
Practice
Solution
Step 1: Understand bitmask role in subsets
Each bit in the bitmask corresponds to an element in the set, showing if it is included (1) or excluded (0).Step 2: Confirm bitmask meaning
This means the bitmask acts like a yes/no flag for each element's presence in the subset.Final Answer:
Each bit indicates whether an element is included in the subset -> Option AQuick Check:
Bitmask bits = element inclusion flags [OK]
- Thinking bits count element frequency
- Confusing bit position with element value
- Assuming bits store element values
Solution
Step 1: Calculate total subsets for 3 elements
Total subsets = 2^3 = 8, so loop must run from 0 to 7 inclusive.Step 2: Check loop range syntax
Using 0..(1 << 3) means 0..8, which covers all subsets correctly.Final Answer:
for mask in 0..(1 << 3) { /* ... */ } -> Option DQuick Check:
Loop range = 0 to 2^n [OK]
- Using 0..n instead of 0..2^n
- Swapping shift operands
- Starting loop from 1 instead of 0
let set = [10, 20];
for mask in 0..(1 << 2) {
let mut subset = Vec::new();
for i in 0..2 {
if (mask & (1 << i)) != 0 {
subset.push(set[i]);
}
}
println!("{:?}", subset);
}Solution
Step 1: Understand mask values and subsets
mask runs 0 to 3 (0b00 to 0b11). For each mask, bits decide which elements to include.Step 2: Map masks to subsets
0b00 = [], 0b01 = [10], 0b10 = [20], 0b11 = [10, 20]. Output prints subsets in this order.Final Answer:
[ ] [10] [20] [10, 20] -> Option AQuick Check:
Bitmask order = empty to full subset [OK]
- Mixing subset order
- Confusing bit positions
- Printing subsets in wrong sequence
let set = [1, 2, 3];
for mask in 0..(1 << 3) {
let mut subset = Vec::new();
for i in 1..3 {
if (mask & (1 << i)) != 0 {
subset.push(set[i]);
}
}
println!("{:?}", subset);
}Solution
Step 1: Check inner loop range
Inner loop runs i from 1 to 2, missing i=0, so first element is never checked.Step 2: Correct loop start
It should be 0..3 to include all elements at indices 0,1,2.Final Answer:
Inner loop should start from 0, not 1 -> Option BQuick Check:
Loop indices must cover all elements [OK]
- Starting inner loop at 1
- Misusing bitwise operators
- Declaring vector outside loop causing accumulation
Solution
Step 1: Understand duplicates in subsets
Duplicates cause repeated subsets if elements are identical and treated independently.Step 2: Use sorting and skip logic
Sorting groups duplicates together. Skipping adding an element if it equals previous and previous bit is not set avoids duplicates.Step 3: Compare other options
Using HashSet works but is less efficient. Increasing bitmask range or ignoring duplicates is incorrect.Final Answer:
Sort the set first and skip adding elements if they are the same as previous and previous bit is not set -> Option CQuick Check:
Sort + skip duplicates = unique subsets [OK]
- Not sorting before subset generation
- Relying on bitmask alone to remove duplicates
- Using larger bitmask range incorrectly
Ready to prove it?
Pass the test and this pattern moves unknown
