LeetCode - Subsets II
Problem statement
Given an integer array nums that may contain duplicates, return all possible subsets (the power set).
The solution set must not contain duplicate subsets. Return the solution in any order.
Problem statement taken from: https://leetcode.com/problems/subsets-ii.
Example 1:
Input: nums = [1, 2, 2]
Output: [[], [1], [1, 2], [1, 2, 2], [2], [2, 2]]
Example 2:
Input: nums = [0]
Output: [[], [0]]
Constraints:
- 1 <= nums.length <= 10
- -10 <= nums[i] <= 10
Explanation
Backtracking
The approach for this problem is similar to our previous blog LeetCode Subsets. The only difference is we need to exclude duplicate elements here while generating the subset.
First, we will sort the nums array. We can either exclude the duplicate elements while recursively calling the subset generator function or we can mark the subset as a Set (Set is an abstract data type that can store unique values).
Let's check the algorithm first.
// subsetsWithDup(nums) function
- sort nums array sort(nums.begin(),nums.end())
- initialize vector<int> subset
set<vector<int>> result
vector<vector<int>> answer
- call util function subsetsUtil(nums, result, subset, 0)
- push set result in vector array
loop for(auto it:result)
answer.push_back(it)
- return answer
// subsetsUtil(nums, result, subset, index) function
- insert subset in result
result.insert(subset)
- loop for i = index; i < nums.size(); i++
- subset.push_back(nums[i])
- subsetsUtil(nums, result, subset, i + 1)
- subset.pop_back()
Let's check out our solutions in C++, Golang, and Javascript. Note: In the C++ solution the subset is a Set, while in Golang and Javascript it's a normal array and we have ignored the duplicates.
C++ solution
class Solution {
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
sort(nums.begin(),nums.end());
vector<int> subset;
set<vector<int>> result;
subsetsUtil(nums, result, subset, 0);
vector<vector<int>> answer;
for(auto it:result){
answer.push_back(it);
}
return answer;
}
public:
void subsetsUtil(vector<int>& nums, set<vector<int>>& result, vector<int>& subset, int index) {
result.insert(subset);
for(int i = index; i < nums.size(); i++){
subset.push_back(nums[i]);
subsetsUtil(nums, result, subset, i + 1);
subset.pop_back();
}
return;
}
};
Golang solution
func subsetsUtils(nums, subset []int, result *[][]int) {
cp := make([]int, len(subset))
copy(cp, subset)
*result = append(*result, cp)
for i := 0; i < len(nums); i++ {
subsetsUtils(nums[i+1:], append(subset, nums[i]), result)
for ; i < len(nums)-1 && nums[i] == nums[i+1]; i++ {
}
}
}
func subsetsWithDup(nums []int) [][]int {
sort.Ints(nums)
var result [][]int
subset := make([]int, 0, len(nums))
subsetsUtils(nums, subset, &result)
return result
}
Javascript solution
var subsetsWithDup = function(nums) {
nums.sort((a, b) => a - b);
const result = [];
subsetsUtils(0, []);
return result;
function subsetsUtils (index, array) {
result.push([...array]);
for (let i = index; i < nums.length; i++) {
if (i > index && nums[i] == nums[i - 1]) {
continue;
}
array.push(nums[i]);
subsetsUtils(i + 1, array);
array.pop();
}
}
};
Dry Run
Let's dry-run our algorithm to see how the solution works.
Input: nums = [1, 2, 2]
Step 1: sort(nums.begin(),nums.end())
nums = [1, 2, 3]
Step 2: initialize vector<int> subset
set<vector<int>> result
Step 3: subsetsUtil(nums, result, subset, 0)
// in subsetsUtils function
Step 4: result.push_back(subset)
result.push_back([])
result = [[]]
loop for i = index, i < nums.size()
i = 0
0 < 3
true
subset.push_back(nums[i])
subset.push_back(nums[0])
subset.push_back(1)
subset = [1]
subsetsUtil(nums, res, subset, i + 1)
subsetsUtil([1, 2, 2], [[]], [1], 0 + 1)
subsetsUtil([1, 2, 2], [[]], [1], 1)
Step 5: result.push_back(subset)
result.push_back([1])
result = [[], [1]]
loop for i = index, i < nums.size()
i = 1
1 < 3
true
subset.push_back(nums[i])
subset.push_back(nums[1])
subset.push_back(2)
subset = [1, 2]
subsetsUtil(nums, res, subset, i + 1)
subsetsUtil([1, 2, 2], [[], [1]], [1, 2], 1 + 1)
subsetsUtil([1, 2, 2], [[], [1]], [1, 2], 2)
Step 6: result.push_back(subset)
result.push_back([1, 2])
result = [[], [1], [1, 2]]
loop for i = index, i < nums.size()
i = 2
2 < 3
true
subset.push_back(nums[i])
subset.push_back(nums[2])
subset.push_back(2)
subset = [1, 2, 2]
subsetsUtil(nums, res, subset, i + 1)
subsetsUtil([1, 2, 2], [[], [1], [1, 2]], [1, 2, 2], 2 + 1)
subsetsUtil([1, 2, 2], [[], [1], [1, 2]], [1, 2, 2], 3)
Step 7: result.push_back(subset)
result.push_back([1, 2, 3])
result = [[], [1], [1, 2], [1, 2, 3]]
loop for i = index, i < nums.size()
i = 3
3 < 3
false
Step 8: Here we backtrack to last line of Step 6 where
i = 2
subset = [1, 2, 2]
We execute the next line
subset.pop()
subset = [1, 2]
Step 9: We backtrack to last line of Step 5 where
i = 1
subset = [1, 2]
We execute the next line
subset.pop()
subset = [1]
Step 10: For loop continues where we execute
loop for i = index, i < nums.size()
i = 2
i < nums.size()
2 < 3
true
subset.push_back(nums[i])
subset.push_back(nums[2])
subset.push_back(2)
subset = [1, 2]
subsetsUtil(nums, res, subset, i + 1)
subsetsUtil([1, 2, 2], [[], [1], [1, 2]], [1, 2], 2 + 1)
subsetsUtil([1, 2, 2], [[], [1], [1, 2]], [1, 2], 3)
Step 11: result.push_back(subset)
result.push_back([1, 2])
result = [[], [1], [1, 2], [1, 2, 2]]
loop for i = index, i < nums.size()
i = 3
3 < 3
false
Step 12: Here we backtrack to last line of Step 3 where
i = 0
subset = [1]
We execute the next line
subset.pop()
subset = []
Step 13: For loop continues where we execute
loop for i = index, i < nums.size()
i = 1
i < nums.size()
1 < 3
true
subset.push_back(nums[i])
subset.push_back(nums[1])
subset.push_back(2)
subset = [2]
subsetsUtil(nums, res, subset, i + 1)
subsetsUtil([1, 2, 2], [[], [1], [1, 2]], [2], 1 + 1)
subsetsUtil([1, 2, 2], [[], [1], [1, 2]], [2], 2)
Step 14: result.push_back(subset)
result.push_back([2])
result = [[], [1], [1, 2], [1, 2, 2], [1, 2], [2]]
loop for i = index, i < nums.size()
i = 2
2 < 3
true
subset.push_back(nums[i])
subset.push_back(nums[2])
subset.push_back(2)
subset = [2, 2]
subsetsUtil(nums, res, subset, i + 1)
subsetsUtil([1, 2, 2], [[], [1], [1, 2], [2]], [2, 2], 2 + 1)
subsetsUtil([1, 2, 2], [[], [1], [1, 2], [2]], [2, 2], 3)
Step 15: result.push_back(subset)
result.push_back([2, 2])
result = [[], [1], [1, 2], [1, 2, 2], [2], [2, 2]]
loop for i = index, i < nums.size()
i = 3
3 < 3
false
Step 16: Here we backtrack to last line of Step 14 where
i = 2
subset = [2, 2]
We execute the next line
subset.pop()
subset = [2]
Step 17: Here we backtrack to last line of Step 13 where
i = 1
subset = [2]
We execute the next line
subset.pop()
subset = []
Step 18: For loop continues where we execute
loop for i = index, i < nums.size()
i = 2
i < nums.size()
2 < 3
true
subset.push_back(nums[i])
subset.push_back(nums[2])
subset.push_back(2)
subset = [2]
subsetsUtil(nums, res, subset, i + 1)
subsetsUtil([1, 2, 2], [[], [1], [1, 2], [2], [2, 2]], [2], 2 + 1)
subsetsUtil([1, 2, 2], [[], [1], [1, 2], [2], [2, 2]], [2], 3)
Step 19: result.push_back(subset)
result.push_back([2])
result = [[], [1], [1, 2], [1, 2, 2], [2], [2, 2]]
loop for i = index, i < nums.size()
i = 3
3 < 3
false
Step 20: We have no more stack entries left. We return to the main function.
Step 21: for(auto it:result){
answer.push_back(it);
}
We push result Set to answer Vector.
Step 22: return answer
So we return the answer as [[], [1], [1, 2], [1, 2, 2], [2], [2, 2]].