Subsets Generation Using Bitmask in DSA C++ - Time & Space Complexity
We want to understand how the time needed to generate all subsets grows as the input size increases.
Specifically, how does the number of operations change when using bitmask to create subsets?
Analyze the time complexity of the following code snippet.
for (int mask = 0; mask < (1 << n); mask++) {
vector subset;
for (int i = 0; i < n; i++) {
if (mask & (1 << i)) {
subset.push_back(nums[i]);
}
}
// process subset
}
This code generates all subsets of an array of size n using bitmasking.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The outer loop runs through all possible bitmasks (from 0 to 2^n - 1), generating each subset.
- How many times: The outer loop runs 2^n times, and for each, the inner loop runs n times to check which elements to include.
As n grows, the number of subsets doubles each time, and for each subset, we check n elements.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 10 * 2^10 = 10,240 operations |
| 100 | About 100 * 2^100 (a huge number, very large) |
| 1000 | About 1000 * 2^1000 (impractical to compute) |
Pattern observation: The operations grow exponentially with n, doubling the work for each additional element.
Time Complexity: O(n * 2^n)
This means the time needed grows very fast, doubling for each new element, and checking each element for every subset.
[X] Wrong: "Generating subsets takes only O(2^n) time because there are 2^n subsets."
[OK] Correct: Each subset requires checking up to n elements to build it, so the total work is n times 2^n, not just 2^n.
Understanding this complexity helps you explain why subset generation is expensive and when it is practical to use in real problems.
"What if we used recursion instead of bitmasking to generate subsets? 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 over all bitmasks from 0 to 2^n - 1, we get all possible subsets of the set.Final Answer:
All possible subsets of the set -> Option BQuick Check:
Bitmask = subsets generator [OK]
- Thinking bitmask generates only one subset
- Confusing subsets with permutations
- Assuming bitmask sorts elements
n?Solution
Step 1: Understand bitmask range
Bitmask ranges from 0 to 2^n - 1, which is (1 << n) - 1 in C++.Step 2: Correct loop syntax
The loop must run from 0 to less than (1 << n) to cover all subsets.Final Answer:
for (int mask = 0; mask < (1 << n); mask++) -> Option CQuick Check:
Loop runs 0 to 2^n-1 [OK]
- Using mask <= n instead of mask < (1 << n)
- Starting loop from 1 instead of 0
- Using mask < n which is too small
std::vector<int> nums = {1, 2};
for (int mask = 0; mask < (1 << 2); mask++) {
std::cout << "Subset: ";
for (int i = 0; i < 2; i++) {
if (mask & (1 << i)) std::cout << nums[i] << " ";
}
std::cout << std::endl;
}Solution
Step 1: Identify bitmask values and subsets
For n=2, masks go 0 to 3:
0 (00) no elements,
1 (01) includes nums[0]=1,
2 (10) includes nums[1]=2,
3 (11) includes nums[0]=1 and nums[1]=2.Step 2: Match output lines to subsets
Output lines print subsets in order:
"Subset: " (empty),
"Subset: 1 ",
"Subset: 2 ",
"Subset: 1 2 "Final Answer:
Subset: Subset: 1 Subset: 2 Subset: 1 2 -> Option AQuick Check:
Bitmask 0 to 3 prints subsets in order [OK]
- Mixing order of subsets
- Printing elements in wrong bit order
- Skipping empty subset output
std::vector<int> nums = {1, 2, 3};
for (int mask = 0; mask <= (1 << nums.size()); mask++) {
for (int i = 0; i < nums.size(); i++) {
if (mask & (1 << i)) std::cout << nums[i] << " ";
}
std::cout << std::endl;
}Solution
Step 1: Analyze loop range for bitmask
The bitmask must go from 0 to 2^n - 1, so mask < (1 << n). Using <= causes one extra iteration beyond valid range.Step 2: Check other parts
Inner loop from 0 is correct, bitwise AND & is correct, vector type is fine.Final Answer:
The loop condition should be mask < (1 << nums.size()), not <=. -> Option DQuick Check:
Loop must run 0 to 2^n-1 [OK]
- Using <= instead of < in loop condition
- Replacing bitwise & with logical &&
- Starting inner loop at 1 instead of 0
nums = {1, 2, 2}, which code modification correctly generates all unique subsets using bitmask technique in C++?Solution
Step 1: Understand duplicate issue in subsets
With duplicates in input, bitmask generates duplicate subsets. Sorting subsets before storing helps identify duplicates.Step 2: Use std::set to store unique subsets
Storing sorted subsets in a set automatically removes duplicates, ensuring unique subsets only.Final Answer:
Use a std::set<std::vector<int>> to store subsets and sort each subset before insertion. -> Option AQuick Check:
Sort subsets + set = unique subsets [OK]
- Not sorting subsets before inserting in set
- Using map instead of set for uniqueness
- Ignoring duplicates without sorting
Ready to prove it?
Pass the test and this pattern moves unknown
