🧠
Brute Force (Pure Recursion)
💡 Starting with brute force helps understand the problem deeply by exploring all subset possibilities explicitly, even if it's not the most efficient.
Intuition
We recursively decide for each element whether to include it in the current subset or not, exploring all 2^n combinations.
Algorithm
- Start with an empty subset and index 0.
- At each index, recursively explore two paths: include the current element or exclude it.
- When index reaches the end, add the current subset to the results.
- Return all collected subsets.
💡 The recursion tree doubles at each step, which can be hard to visualize initially, but it systematically covers all subsets.
def subsets(nums):
res = []
def backtrack(i, path):
if i == len(nums):
res.append(path[:])
return
# Exclude nums[i]
backtrack(i + 1, path)
# Include nums[i]
path.append(nums[i])
backtrack(i + 1, path)
path.pop()
backtrack(0, [])
return res
# Driver code
if __name__ == '__main__':
print(subsets([1,2,3]))
Line Notes
def subsets(nums):Defines the main function to generate subsets
def backtrack(i, path):Helper recursive function with current index and current subset
if i == len(nums):Base case: all elements considered, add current subset copy to results
backtrack(i + 1, path)Explore path excluding current element
path.append(nums[i])Include current element before next recursion
path.pop()Backtrack to remove last element and explore other paths
backtrack(0, [])Start recursion from index 0 with empty subset
import java.util.*;
public class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
backtrack(nums, 0, new ArrayList<>(), res);
return res;
}
private void backtrack(int[] nums, int i, List<Integer> path, List<List<Integer>> res) {
if (i == nums.length) {
res.add(new ArrayList<>(path));
return;
}
// Exclude nums[i]
backtrack(nums, i + 1, path, res);
// Include nums[i]
path.add(nums[i]);
backtrack(nums, i + 1, path, res);
path.remove(path.size() - 1);
}
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(sol.subsets(new int[]{1,2,3}));
}
}
Line Notes
public List<List<Integer>> subsets(int[] nums)Main method to generate subsets
private void backtrack(int[] nums, int i, List<Integer> path, List<List<Integer>> res)Recursive helper with current index and subset
if (i == nums.length)Base case: all elements processed, add copy of path
backtrack(nums, i + 1, path, res);Explore excluding current element
path.add(nums[i]);Include current element before recursion
path.remove(path.size() - 1);Backtrack by removing last element
public static void main(String[] args)Driver code to test the solution
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
void backtrack(vector<int>& nums, int i, vector<int>& path, vector<vector<int>>& res) {
if (i == nums.size()) {
res.push_back(path);
return;
}
// Exclude nums[i]
backtrack(nums, i + 1, path, res);
// Include nums[i]
path.push_back(nums[i]);
backtrack(nums, i + 1, path, res);
path.pop_back();
}
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> res;
vector<int> path;
backtrack(nums, 0, path, res);
return res;
}
};
int main() {
Solution sol;
vector<int> nums = {1,2,3};
vector<vector<int>> result = sol.subsets(nums);
for (auto &subset : result) {
cout << "[";
for (int i = 0; i < subset.size(); i++) {
cout << subset[i];
if (i != subset.size() - 1) cout << ",";
}
cout << "] ";
}
cout << endl;
return 0;
}
Line Notes
void backtrack(vector<int>& nums, int i, vector<int>& path, vector<vector<int>>& res)Recursive helper with current index and current subset
if (i == nums.size())Base case: all elements considered, add current subset
backtrack(nums, i + 1, path, res);Explore excluding current element
path.push_back(nums[i]);Include current element before recursion
path.pop_back();Backtrack by removing last element
vector<vector<int>> subsets(vector<int>& nums)Main function to initiate recursion
int main()Driver code to test the solution
function subsets(nums) {
const res = [];
function backtrack(i, path) {
if (i === nums.length) {
res.push([...path]);
return;
}
// Exclude nums[i]
backtrack(i + 1, path);
// Include nums[i]
path.push(nums[i]);
backtrack(i + 1, path);
path.pop();
}
backtrack(0, []);
return res;
}
// Driver code
console.log(subsets([1,2,3]));
Line Notes
function subsets(nums)Main function to generate all subsets
function backtrack(i, path)Recursive helper with current index and current subset
if (i === nums.length)Base case: all elements processed, add copy of path
backtrack(i + 1, path);Explore excluding current element
path.push(nums[i]);Include current element before recursion
path.pop();Backtrack by removing last element
backtrack(0, []);Start recursion from index 0 with empty subset
TimeO(n * 2^n)
SpaceO(n * 2^n)
There are 2^n subsets, and each subset can take up to O(n) space to store. The recursion tree has 2^n leaves.
💡 For n=20, 2^20 is about 1 million, so this approach is feasible but can be slow for larger n.
Interview Verdict: Accepted
Though exponential, this approach is straightforward and accepted for n up to around 20, which is typical for subset generation problems.