Pattern #15
Backtracking
EssentialChoose, explore, undo. Build all valid combinations or search with pruning.
When to use
You must generate all solutions (subsets, permutations, combinations) or search a decision tree with constraints.
Recognition cues
- Subsets / permutations / combinations
- Word search / N-Queens style constraints
- Path building with undo
Common pitfalls
- Forgetting to undo (mutate path without pop)
- Wrong start index causing duplicates or misses
- Not pruning when the partial path is already invalid
90-second recognition drill
Which pattern fits best?
- Subsets / permutations / combinations
- Word search / N-Queens style constraints
- Path building with undo
Interactive
Mental model
A full worked walkthrough of the invariant. Pause, scrub the dots, or use ← →. Aim to narrate each step yourself.
path = []
Permutations of [1,2,3]: choose, explore, undo.
How to think about it
At each step, try a choice, recurse, then undo. The call stack is the current path. Prune when the partial solution cannot lead to a valid complete one. Order choices carefully to avoid duplicate outputs.
Template shapes
| Shape | Core move | Notes |
|---|---|---|
| Subsets | Include or skip each element | Push path copy at every node |
| Permutations | Swap or used[] mask | Length == n → record |
| Combinations | Start index i | Avoid reordering duplicates |
Complexity baseline
Output-sensitive: often O(n·#solutions). Extra space O(n) recursion depth.
From template to problem
- Define the state (path, index, remaining target, used mask).
- Base case: record a complete valid path.
- Loop choices; push; recurse; pop.
- Add pruning (sum too large, cell visited, etc.).
Template
Same skeleton in TypeScript, Python, and C#. Adapt the invariant; keep the structure.
/** Backtracking template: subsets (choose / skip via start index). */
export function subsets(nums: number[]): number[][] {
const res: number[][] = [];
const path: number[] = [];
const dfs = (start: number) => {
res.push([...path]);
for (let i = start; i < nums.length; i++) {
path.push(nums[i]!);
dfs(i + 1);
path.pop();
}
};
dfs(0);
return res;
}
/** Backtracking template: subsets (choose / skip via start index). */
export function subsets(nums: number[]): number[][] {
const res: number[][] = [];
const path: number[] = [];
const dfs = (start: number) => {
res.push([...path]);
for (let i = start; i < nums.length; i++) {
path.push(nums[i]!);
dfs(i + 1);
path.pop();
}
};
dfs(0);
return res;
}
- 1#39 Combination SumGuidemedium
- 2#46 PermutationsGuidemedium
- 3#78 SubsetsGuidemedium
- 4#79 Word SearchGuidemedium
- 5#90 Subsets IIGuidemedium
- 6#131 Palindrome PartitioningGuidemedium