Subsets
Given an array of distinct integers, return every possible subset — the power set.
A subset is formed by choosing, independently for each element, whether to include it. That includes taking none of them (the empty subset) and taking all of them, both of which must appear in the output.
Subsets are sets, so order within a subset carries no meaning and no subset may be listed twice. The order of the subsets themselves does not matter.
- 1 <= nums.length <= 10
- -10 <= nums[i] <= 10
- All integers in nums are distinct
- The output contains exactly 2ⁿ subsets, including the empty one
- Subsets may be returned in any order
nums = [1,2,3][[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]nums = [0][[],[0]]nums = [1,2][[],[1],[2],[1,2]]nums = [-1,5][[],[-1],[5],[-1,5]]A subset is one yes/no decision per element: take it, or leave it. Make those decisions one element at a time, and every full set of decisions is one subset. That's a tree — n levels deep, 2ⁿ leaves, one subset per leaf. Generating all subsets means walking every path to the bottom.
Every problem in this section is the same loop — three beats: choose, explore, un-choose.
def backtrack(state):
if done(state):
save(state) # record the finished answer
return
for choice in choices(state): # what can I pick right now?
if not ok(choice):
continue # prune: skip bad picks early
apply(choice) # CHOOSE
backtrack(next_state) # EXPLORE
undo(choice) # UN-CHOOSE: put it backOnly four slots change between problems: done, choices, ok (pruning), and how you finish (save every answer, or return True at the first one). Fill those and the problem is solved.
i, or skip it.i.i == n) → save the path.def subsets(nums):
res, path = [], []
def backtrack(i):
if i == len(nums):
res.append(list(path)) # save a COPY, not path itself
return
path.append(nums[i]) # CHOOSE: take it
backtrack(i + 1) # EXPLORE
path.pop() # UN-CHOOSE
backtrack(i + 1) # EXPLORE: skip it
backtrack(0)
return resTwo things bite everyone: save list(path) (a copy) — appending path stores a reference that keeps changing. And the pop() goes between the two calls, so the skip branch starts clean.
Trace [1,2]: take 1 → take 2 → save [1,2]; drop 2 → save [1]; drop 1 → take 2 → save [2]; drop 2 → save []. Result [[1,2],[1],[2],[]].
The Recursion Tree
Visualizing how the algorithm branches for each decision.
TREE STRUCTURE
The tree grows from left to right. Each level corresponds to one number in the input. For each number, we branch Up (+ Include) or Down (- Exclude).
DFS TRAVERSAL
Backtracking is simply a Depth First Search. We go as deep as possible into one reality, then step back to the last fork and try the other path.