150NeetCode 150

74.Subsets II

MediumBacktracking · 4/9

Problem

Given an integer array nums that may contain duplicates, return all possible subsets (the power set).

The result must not contain duplicate subsets. [1,2] built from the first 2 and [1,2] built from the second 2 are the same subset and must appear only once. Subsets may be returned in any order.

Examples

Input:  nums = [1, 2, 2]
Output: [[], [1], [1,2], [1,2,2], [2], [2,2]]
Explanation: Plain Subsets would produce [2] and [1,2] twice; they appear once here.
Input:  nums = [0]
Output: [[], [0]]

Constraints

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10

💡 Key Insight

Sort so equal values sit together. Then duplicates only arise when, at the same level of the decision tree, you start a branch with a value equal to the one you just tried. The rule "at a given level, skip nums[i] if i > start and nums[i] == nums[i - 1]" keeps exactly one branch per distinct value per level — and still allows [2,2] because the second 2 is chosen at a deeper level.

Brute Force with a Set

TimeO(n · 2^n)SpaceO(n · 2^n)

Intuition

Generate all 2^n subsets as in Subsets, but sort first so equal multisets produce identical lists ([1,2] never appears as [2,1]). Then a HashSet<List<Integer>> removes duplicates automatically.

Algorithm

  1. Sort nums.
  2. For each mask in 0 .. 2^n - 1, build the subset of chosen indices (in increasing order).
  3. Add it to a Set<List<Integer>>.
  4. Return the set as a list.

Visualization

nums sorted = [1,2,2]

mask 010 -> [2]      new
mask 100 -> [2]      already in set, ignored
mask 011 -> [1,2]    new
mask 101 -> [1,2]    already in set, ignored
mask 110 -> [2,2]    new

Code

java
import java.util.*;

class Solution {
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        Arrays.sort(nums); // equal subsets now produce identical lists
        int n = nums.length;
        Set<List<Integer>> seen = new HashSet<>();
        for (int mask = 0; mask < (1 << n); mask++) {
            List<Integer> subset = new ArrayList<>();
            for (int i = 0; i < n; i++) {
                if (((mask >> i) & 1) == 1) subset.add(nums[i]);
            }
            seen.add(subset); // duplicates are dropped by the set
        }
        return new ArrayList<>(seen);
    }
}

Complexity

  • Time: O(n · 2^n) — every mask is built and hashed in O(n).
  • Space: O(n · 2^n) — the set stores every distinct subset.

Complexity Comparison

Approach Time Space (extra)
Brute Force + Set O(n · 2^n) O(n · 2^n)
Iterative O(n · 2^n) O(n)
Backtracking with Skip O(n · 2^n) O(n)

Common Pitfalls

  • Forgetting to sort — the skip rule only works when duplicates are adjacent, and the set approach needs a canonical order.
  • Writing i > 0 instead of i > start in the skip check — that wrongly forbids [2,2] by skipping the second 2 at a deeper level.
  • Using a set without sorting: [1,2] and [2,1] would be treated as different lists.
  • Adding path itself rather than a copy.