Explore → Undo → Explore
Imagine you're in a maze with a fork in every corridor. At each fork you pick a direction and keep walking. Hit a dead end? You don't teleport out — you walk back to the last fork you remember, and try the next unexplored corridor instead. That's backtracking, in full.
Backtracking is a systematic way to explore all candidate solutions and abandon ("prune") paths the instant they become invalid — a wall in the maze, a queen already under attack, brackets that can never balance. You don't wait to hit the actual dead end; you stop walking a doomed corridor as soon as you can tell it's doomed. It's depth-first search on a decision tree with early termination.
CHOOSE — Make a decision (place a queen, pick a parenthesis)
EXPLORE — Recurse with the new state
PRUNE — Skip invalid branches early
UNCHOOSE — Undo the decision and try the next option
DFS on a Decision Tree
Start
Choices → Constraints → Goal
The Mental Checklist
CHOICES
What options do I have at each step?
CONSTRAINTS
Which choices are invalid? Prune them!
GOAL
When do I record a solution and return?
Every backtracking problem below is the exact same skeleton wearing different clothes. Learn this shape once:
Subsets (Power Set)
Simplest Backtracking There Is
The simplest backtracking: every element is either taken or skipped. Record every intermediate state — there's no "wrong" subset, so nothing needs pruning.
Complexity: 2 choices (take/skip) per element, N elements → O(2^N) subsets, each costing O(N) to copy — O(N · 2^N) total.
Permutations
Order Matters Now
Unlike subsets, every element must appear exactly once, but order matters — [1,2] and [2,1] are different results. Track which indices are already used to prune re-selecting them.
Complexity: N choices, then N-1, then N-2... → O(N!) permutations, each costing O(N) to copy — O(N · N!) total.
Combination Sum
Prune by Running Total
Find every combination of candidates that sums exactly to a target — the same candidate can be reused unlimited times. The prune is arithmetic: the moment the running sum overshoots the target, that whole branch is dead.
Key detail: recursing with i (not i + 1) is what allows reusing the same candidate again — passing i + 1 instead turns this into Combination Sum II (each candidate used at most once).
Generate Parentheses
The Pruning Rule
The constraint is simple: we can add '(' if open < n, and ')' if close < open. This ensures well-formed parentheses.
Complexity: the count of valid sequences is the N-th Catalan number, C(N) = (2N choose N) / (N + 1) — much smaller than the naive 2^(2N) because most branches get pruned early.
N-Queens
Place N queens on an N×N board so no two attack each other. The classic backtracking showcase: one queen per row, prune by column + diagonal conflicts.
Key insight: The isSafe check is the pruning step. Without it, we'd generate all N^N board arrangements — with it, we only explore valid partial solutions.
How to Spot This Pattern
🔎 "Generate all..." combinations, permutations, or subsets.
🔎 Constraints look tiny — n ≤ ~20 is a strong tell that exponential search is expected.
🔎 "Find all valid ways to place / arrange / partition..."
Named problems: Subsets · Permutations · Combination Sum · Palindrome Partitioning · N-Queens · Sudoku Solver · Word Search
Common Mistakes
Forgetting to Copy the Path
result.push(path) stores a reference to the same array you keep mutating — every recorded "solution" ends up identical (usually empty) by the time you're done. Always push a copy: result.push([...path]).
Pruning Too Late
Checking validity inside the recursive call (after already choosing and recursing) wastes an entire function call and stack frame on a branch you already know is dead. Check before you recurse, so invalid choices are skipped, not entered.
Mutating Shared State Without Undo
Every CHOOSE needs a matching UNCHOOSE. Forgetting to pop/reset after the recursive call returns leaves stale state bleeding into sibling branches — the classic symptom is "it works for the first path, then everything after is wrong."
Ready to Practice?
"Backtracking = DFS + pruning + undo."