The problem. Given an integer array nums that may contain duplicates, return all possible subsets. The answer must not contain duplicate subsets.
nums = [1, 2, 2] -> [[], [1], [1, 2], [1, 2, 2], [2], [2, 2]]
nums = [0] -> [[], [0]]Use the loop-from-start template: the node for a subset tries adding each number at or after start, and every node is itself an answer. Duplicates appear when two equal values are tried at the same level: taking the first 2 and taking the second 2 lead to identical subtrees. Sort first so equal values sit together, then at each level skip a value equal to the one just tried: if (i > start && nums[i] === nums[i − 1]) continue. Taking a 2 and then another 2 deeper down is still allowed — that's i == start.
function subsetsWithDup(nums) { const sorted = [...nums].sort((a, b) => a - b); // so equal subsets look the same const seen = new Set(); const result = []; for (let mask = 0; mask < 1 << sorted.length; mask++) { const s = sorted.filter((_, j) => mask & (1 << j)); const key = s.join(","); if (!seen.has(key)) { seen.add(key); result.push(s); } } return result;}Time O(n · 2ⁿ), space O(n · 2ⁿ) for the set. It builds every repeat only to throw it away.
Every node is a subset; a value equal to the one just tried at the same level is skipped.
function subsetsWithDup(nums) { const a = [...nums].sort((x, y) => x - y); // equal values next to each other const result = []; const path = []; const build = (start) => { result.push([...path]); // every node is a subset for (let i = start; i < a.length; i++) { if (i > start && a[i] === a[i - 1]) continue; // same value at the same level: a repeat path.push(a[i]); build(i + 1); path.pop(); } }; build(0); return result;}Time O(n · 2ⁿ) in the worst case (all distinct), space O(n) besides the output. With many duplicates the tree is much smaller than 2ⁿ, because the repeats are never built.
[2, 2, 2]: [], [2], [2,2], [2,2,2].[4, 4, 1, 4]: sort first, or the skip check misses repeats.i > 0 you'd also forbid [2, 2].used check instead.