150NeetCode 150

71.Subsets

MediumBacktracking · 1/9

Problem

Given an integer array nums of unique elements, return all possible subsets (the power set).

The answer must not contain duplicate subsets, and the subsets may be returned in any order. The empty subset [] and the full array are both valid subsets.

Examples

Input:  nums = [1, 2, 3]
Output: [[], [1], [2], [1,2], [3], [1,3], [2,3], [1,2,3]]
Explanation: 3 elements give 2^3 = 8 subsets.
Input:  nums = [0]
Output: [[], [0]]

Constraints

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10
  • All the numbers of nums are unique.

💡 Key Insight

Every element faces exactly one binary decision: include it or skip it. With n elements that gives 2^n subsets — a full binary decision tree whose leaves are the answers. You can walk that tree with recursion (backtracking), enumerate it with the bits of a counter (bitmask), or build it level by level (cascading). All are optimal because the output itself has size n · 2^n.

Cascading (Iterative)

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

Intuition

Start with [[]]. When a new number arrives, every subset that already exists can either stay as it is or be copied with the new number appended. So processing a number doubles the list.

Algorithm

  1. result = [[]].
  2. For each num in nums:
    • Let size = result.size().
    • For each of the first size subsets, copy it, append num, and add the copy to result.
  3. Return result.

Visualization

start            : [[]]
after num = 1    : [[], [1]]
after num = 2    : [[], [1], [2], [1,2]]
after num = 3    : [[], [1], [2], [1,2], [3], [1,3], [2,3], [1,2,3]]

Code

java
import java.util.*;

class Solution {
    public List<List<Integer>> subsets(int[] nums) {
        List<List<Integer>> result = new ArrayList<>();
        result.add(new ArrayList<>()); // the empty subset
        for (int num : nums) {
            int size = result.size(); // freeze the count before we start adding
            for (int i = 0; i < size; i++) {
                List<Integer> copy = new ArrayList<>(result.get(i));
                copy.add(num);
                result.add(copy);
            }
        }
        return result;
    }
}

Complexity

  • Time: O(n · 2^n) — 2^n subsets are created, each copy costs up to n.
  • Space: O(n · 2^n) — the output itself; no extra working memory beyond one copy.

Complexity Comparison

Approach Time Space (extra)
Cascading O(n · 2^n) O(n)
Bitmask O(n · 2^n) O(n)
Backtracking O(n · 2^n) O(n)

Common Pitfalls

  • Adding path itself instead of new ArrayList<>(path) — every entry ends up pointing at the same (finally empty) list.
  • In the cascading approach, looping i < result.size() while adding to result — an infinite loop. Freeze size first.
  • Forgetting the empty subset — it is part of the power set.
  • Using 1 << n for large n overflows an int past 31; fine here since n <= 10.