C++ Program to Find All Subsets of a Set
void findSubsets(vector& nums, vector& current, int index) recursively builds subsets by choosing to add or skip elements.Examples
How to Think About It
Algorithm
Code
#include <iostream> #include <vector> using namespace std; void findSubsets(const vector<int>& nums, vector<int>& current, int index) { if (index == nums.size()) { cout << "{"; for (int i = 0; i < current.size(); ++i) { cout << current[i]; if (i != current.size() - 1) cout << ", "; } cout << "}\n"; return; } // Exclude current element findSubsets(nums, current, index + 1); // Include current element current.push_back(nums[index]); findSubsets(nums, current, index + 1); current.pop_back(); } int main() { vector<int> nums = {1, 2, 3}; vector<int> current; findSubsets(nums, current, 0); return 0; }
Dry Run
Let's trace the example nums = {1, 2} through the code
Start with index 0 and empty subset
current = {}, index = 0
Exclude nums[0] = 1 and move to index 1
current = {}, index = 1
Exclude nums[1] = 2 and move to index 2 (end)
current = {}, index = 2 -> print {}
Include nums[1] = 2 and move to index 2 (end)
current = {2}, index = 2 -> print {2}
Back to index 0, include nums[0] = 1
current = {1}, index = 1
Exclude nums[1] = 2 and move to index 2 (end)
current = {1}, index = 2 -> print {1}
Include nums[1] = 2 and move to index 2 (end)
current = {1, 2}, index = 2 -> print {1, 2}
| Step | Current Subset | Index |
|---|---|---|
| 1 | {} | 0 |
| 2 | {} | 1 |
| 3 | {} | 2 (print {}) |
| 4 | {2} | 2 (print {2}) |
| 5 | {1} | 1 |
| 6 | {1} | 2 (print {1}) |
| 7 | {1, 2} | 2 (print {1, 2}) |
Why This Works
Step 1: Recursive exploration
The function uses recursion to explore two paths at each element: including it or excluding it, which ensures all subsets are covered.
Step 2: Base case prints subset
When the index reaches the end of the list, the current subset is printed, representing one complete subset.
Step 3: Backtracking
After exploring including an element, the code removes it (backtracks) to explore other subsets without that element.
Alternative Approaches
#include <iostream> #include <vector> using namespace std; int main() { vector<int> nums = {1, 2, 3}; int n = nums.size(); int total = 1 << n; // 2^n subsets for (int mask = 0; mask < total; ++mask) { cout << "{"; bool first = true; for (int i = 0; i < n; ++i) { if (mask & (1 << i)) { if (!first) cout << ", "; cout << nums[i]; first = false; } } cout << "}\n"; } return 0; }
#include <iostream> #include <vector> using namespace std; int main() { vector<int> nums = {1, 2, 3}; vector<vector<int>> subsets = {{}}; for (int num : nums) { int size = subsets.size(); for (int i = 0; i < size; ++i) { vector<int> newSubset = subsets[i]; newSubset.push_back(num); subsets.push_back(newSubset); } } for (auto& subset : subsets) { cout << "{"; for (int i = 0; i < subset.size(); ++i) { cout << subset[i]; if (i != subset.size() - 1) cout << ", "; } cout << "}\n"; } return 0; }
Complexity: O(n * 2^n) time, O(n) space
Time Complexity
There are 2^n subsets for n elements, and printing each subset takes up to O(n) time, so total time is O(n * 2^n).
Space Complexity
The recursion stack and current subset use O(n) space, as at most n elements are stored at once.
Which Approach is Fastest?
Bitmasking is often faster due to iteration and bit operations, but recursion is easier to understand and modify.
| Approach | Time | Space | Best For |
|---|---|---|---|
| Recursive Backtracking | O(n * 2^n) | O(n) | Clear logic, easy to modify |
| Bitmasking | O(n * 2^n) | O(n) | Performance and iteration |
| Iterative Building | O(n * 2^n) | O(n * 2^n) | Avoid recursion, build subsets stepwise |