90. Subsets II
Date: 2026-03-18 Difficulty: Medium Topics: Backtracking, Recursion, Array, Sorting Link: https://leetcode.com/problems/subsets-ii/
Final Solution
class Solution:
def subsetsWithDup(self, nums: List[int]) -> List[List[int]]:
result = []
def backtrack(curr, index):
if index == len(nums):
result.append(curr.copy())
return
# exclude: skip all duplicates of nums[index]
i = index + 1
while i < len(nums) and nums[i] == nums[index]:
i += 1
backtrack(curr, i)
# include
curr.append(nums[index])
backtrack(curr, index + 1)
curr.pop()
nums.sort()
backtrack([], 0)
return resultInitial Issues
- First attempt used
if curr not in result— works but O(n * 2^n) check per insert - Tried skipping duplicates before the include/exclude decision — skips for both branches when it should only skip in the exclude branch
- Needed to sort first so duplicates are adjacent
Key Learnings
Duplicate Handling: Skip in Exclude Branch Only
- Exclude means “I don’t want ANY of this value” → skip all copies
- Include means “I want at least one” → move to index+1 normally, can still include more copies
Why Pruning Prevents Duplicates
For nums = [1, 1, 2], without pruning there are two paths to [1]:
- Exclude
1[0], include1[1]→[1] - Include
1[0], exclude1[1]→[1]
Pruning removes path 1: excluding 1[0] skips past ALL 1s, so you can never get [1] from that branch. [1] can only come from path 2.
Visualization with pruning (nums = [1, 1, 2])
[]
/ \
exclude 1[0] include 1[0]
SKIP to idx 2 [1]
[] / \
/ \ ex 1[1] inc 1[1]
ex 2 inc 2 [1] [1,1]
[] [2] / \ / \
ex inc ex inc
[1] [1,2] [1,1] [1,1,2]
Result: [[], [2], [1], [1,2], [1,1], [1,1,2]] — no duplicates
Same pattern used in Combination Sum II (40)
Complexity
- Time: O(n * 2^n) — 2^n subsets worst case (all unique), O(n) to copy each. Pruning reduces work but doesn’t change worst case
- Space: O(n * 2^n) for output; O(n) auxiliary (recursion depth + curr)
- Sorting: O(n log n) — dominated by backtracking