🧠
Brute Force (Pure Recursion with Deduplication by Set)
💡 This approach introduces the basic idea of generating all subsets by recursion but uses a set to remove duplicates after generation. It is simple but inefficient and helps understand the problem's nature.
Intuition
Generate all subsets by including or excluding each element recursively. Because duplicates exist, store subsets in a set to avoid duplicates.
Algorithm
- Sort the input array to group duplicates together.
- Use recursion to explore two choices at each index: include or exclude the current element.
- At each recursion leaf, add the current subset to a set to avoid duplicates.
- Convert the set to a list and return all unique subsets.
💡 The main challenge is duplicates causing repeated subsets; sorting and using a set after generation is a straightforward but costly way to handle this.
from typing import List
def subsetsWithDup(nums: List[int]) -> List[List[int]]:
nums.sort()
res = set()
def backtrack(i, path):
if i == len(nums):
res.add(tuple(path))
return
# Exclude nums[i]
backtrack(i + 1, path)
# Include nums[i]
backtrack(i + 1, path + [nums[i]])
backtrack(0, [])
return [list(t) for t in res]
# Driver code
if __name__ == '__main__':
print(subsetsWithDup([1,2,2]))
Line Notes
nums.sort()Sort to group duplicates, making it easier to handle them later.
res = set()Use a set to automatically remove duplicate subsets.
if i == len(nums):Base case: when index reaches end, add current subset to results.
res.add(tuple(path))Convert list to tuple to store in set because lists are unhashable.
import java.util.*;
public class Solution {
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
Set<List<Integer>> res = new HashSet<>();
backtrack(nums, 0, new ArrayList<>(), res);
return new ArrayList<>(res);
}
private void backtrack(int[] nums, int index, List<Integer> path, Set<List<Integer>> res) {
if (index == nums.length) {
res.add(new ArrayList<>(path));
return;
}
// Exclude nums[index]
backtrack(nums, index + 1, path, res);
// Include nums[index]
path.add(nums[index]);
backtrack(nums, index + 1, path, res);
path.remove(path.size() - 1);
}
// Driver code
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(sol.subsetsWithDup(new int[]{1,2,2}));
}
}
Line Notes
Arrays.sort(nums);Sort input to group duplicates for easier handling.
Set<List<Integer>> res = new HashSet<>();Use a set to avoid duplicate subsets.
if (index == nums.length)Base case: add current subset to results when end reached.
res.add(new ArrayList<>(path));Add a copy of current subset to results to avoid mutation.
#include <iostream>
#include <vector>
#include <set>
#include <algorithm>
using namespace std;
class Solution {
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
sort(nums.begin(), nums.end());
set<vector<int>> res;
vector<int> path;
backtrack(nums, 0, path, res);
return vector<vector<int>>(res.begin(), res.end());
}
private:
void backtrack(vector<int>& nums, int index, vector<int>& path, set<vector<int>>& res) {
if (index == nums.size()) {
res.insert(path);
return;
}
// Exclude nums[index]
backtrack(nums, index + 1, path, res);
// Include nums[index]
path.push_back(nums[index]);
backtrack(nums, index + 1, path, res);
path.pop_back();
}
};
// Driver code
int main() {
Solution sol;
vector<int> nums = {1,2,2};
vector<vector<int>> result = sol.subsetsWithDup(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
sort(nums.begin(), nums.end());Sort input to group duplicates for easier handling.
set<vector<int>> res;Use a set to automatically remove duplicate subsets.
if (index == nums.size()) {Base case: add current subset to results when end reached.
res.insert(path);Insert current subset into set to avoid duplicates.
function subsetsWithDup(nums) {
nums.sort((a,b) => a - b);
const res = new Set();
function backtrack(i, path) {
if (i === nums.length) {
res.add(path.join(','));
return;
}
// Exclude nums[i]
backtrack(i + 1, path);
// Include nums[i]
backtrack(i + 1, path.concat(nums[i]));
}
backtrack(0, []);
return Array.from(res).map(s => s === '' ? [] : s.split(',').map(Number));
}
// Driver code
console.log(subsetsWithDup([1,2,2]));
Line Notes
nums.sort((a,b) => a - b);Sort input to group duplicates for easier handling.
const res = new Set();Use a set to remove duplicate subsets by storing string keys.
if (i === nums.length) {Base case: add current subset to results when end reached.
res.add(path.join(','));Convert subset to string to store in set since arrays are unhashable.
TimeO(2^n * n) due to generating all subsets and converting to tuples/lists
SpaceO(2^n * n) for storing all subsets in the set
We explore all subsets (2^n). Each subset can be up to length n, and storing in a set requires hashing/conversion.
💡 For n=20, this means over a million subsets, which is very slow and memory-heavy.
Interview Verdict: Accepted but inefficient; not suitable for large inputs
This approach works but is too slow for large inputs. It is useful to understand the problem and correctness before optimizing.