Subsets II

List every subset of numbers that may contain duplicates, without listing the same subset twice. Sort, then never start two branches with the same value at one depth.

Time O(n · 2ⁿ)Space O(n)Code C#
Watch it animatedStep through it, with the running line highlighted. Free with an account.

The idea

Plain subsets backtracking builds a subset once per choice of positions, so [1, 2a] and [1, 2b] come out as two copies of [1, 2]. After sorting, equal values are neighbours. At one depth, the loop decides which value comes next; starting with the second 2 would only rebuild what starting with the first 2 already built. So skip nums[i] when it equals nums[i - 1] and i > start. Going deeper is still allowed, which is how [2, 2] gets built.

Code

public IList<IList<int>> SubsetsWithDup(int[] nums) {
    Array.Sort(nums); // equal values now sit side by side
    var result = new List<IList<int>>();
    Backtrack(nums, 0, new List<int>(), result);
    return result;
}

void Backtrack(int[] nums, int start, List<int> path, List<IList<int>> result) {
    result.Add(new List<int>(path)); // every path is a subset
    for (int i = start; i < nums.Length; i++) {
        if (i > start && nums[i] == nums[i - 1]) continue; // same value already tried at this depth
        path.Add(nums[i]);
        Backtrack(nums, i + 1, path, result);
        path.RemoveAt(path.Count - 1);
    }
}

Tips

In the .NET library

new List<int>(path) stores a copy. Adding path itself would store one shared list that ends up empty after all the RemoveAt calls.