Generating Subsets
The backtracking pattern that generates all 2ⁿ subsets with a take/leave choice for each element.
#How does it work?
A set of n elements has 2ⁿ subsets, because each element has two options: it is either in the subset or not. These choices form a binary decision tree.
With backtracking, each level decides on one element. First the element is taken and we continue with the rest; when that branch is done the element is removed (undone) and the branch without it is tried. Every leaf of the tree is a subset.
The same pattern becomes a powerful tool once constraints are added: when looking for subsets with a given sum, a branch whose sum is already too large is never expanded (pruning).
#Try it yourself
Found (0)
We generate every subset of the set of numbers 1 … n. Each element has two branches: take it (+) or leave it (−).
#Step by step
- 01Start at the first element with an empty chosen list.
- 02Add the element to the list and move to the next element.
- 03When that branch is done, remove the element and continue without it.
- 04When every element has been decided (a leaf), record the list as a subset.
#Example code
The same algorithm in 8 languages. Every program is compiled, run and checked to print the same output.
def subsets(items: list) -> list:
out, chosen = [], []
def visit(i: int) -> None:
if i == len(items):
out.append(list(chosen))
return
chosen.append(items[i]) # take items[i]
visit(i + 1)
chosen.pop() # leave it (backtrack)
visit(i + 1)
visit(0)
return out
for s in subsets([1, 2, 3]):
print("{" + ", ".join(map(str, s)) + "}")#Complexity
| Operation | Time |
|---|---|
| Number of subsets | 2ⁿ |
| Generating them all | O(n · 2ⁿ) |
| Memory (recursion) | O(n) |
| Bit mask alternative | O(n · 2ⁿ) |
Writing each subset takes O(n), so the total is O(n · 2ⁿ); the output itself is that large, so nothing can be faster. Alternatively the numbers 0 to 2ⁿ − 1 can serve as bit masks: if bit i is 1, element i is in the subset.
#When to use it?
Small problems where every combination must be tried: subset sum, team selection, feature selection, combinatorial testing and puzzle search. As n grows, 2ⁿ explodes, so pruning or DP becomes necessary.
#Practice
Log in to answer questions and save your progress.
Easy
How many subsets does a set of n elements have?
- 12ⁿ
- 2n!
- 3n²
- 42n
What does each leaf of the decision tree represent?
- 1A subset
- 2An element
- 3A permutation
- 4An error
Why must the element be removed after the "take" branch?
- 1To try the "leave" branch with the same list
- 2To free memory
- 3To sort the elements
- 4It isn't needed
Medium
For {1, 2, 3}, trying "take" first, which subset comes second?
- 1{1, 2}
- 2{1, 3}
- 3{2, 3}
- 4{}
Hard
What is a non-recursive way to generate all subsets?
- 1Using the numbers 0 to 2ⁿ − 1 as bit masks
- 2Sorting the elements
- 3Pushing all elements onto a stack
- 4Running a binary search
#Up next
Lessons that build on this one: