Backtracking
Build a solution one choice at a time. Before each choice, check it against what is already placed; if nothing works, undo the last choice and try its next option. That undo is the whole trick: it turns a blind enumeration into a search that abandons a dead branch the moment it is dead. Sudoku is the same loop with a richer constraint: at each empty cell try the digits in order, keep the first that is absent from its row, column and box, and when nothing fits, clear the cell and hand the previous one its next digit.
Find a path from the top-left to the bottom-right through open cells (1), never through walls (0). Walk depth-first, trying down, right, up, left in that order, and retreat from any cell with no way forward.
Check your understanding
The player pauses before each decision in this run and asks what happens next. Here are all 6, with their answers.
At (0, 0), trying down, right, up, left. Where next?
Answer: down. down is the first direction in order that leads to an open, unvisited cell.
At (1, 0), trying down, right, up, left. Where next?
Answer: Dead end, retreat. Nothing open remains around it. Backtracking drops it from the path.
At (0, 0), trying down, right, up, left. Where next?
Answer: right. right is the first direction in order that leads to an open, unvisited cell.
At (0, 1), trying down, right, up, left. Where next?
Answer: right. right is the first direction in order that leads to an open, unvisited cell.
At (0, 2), trying down, right, up, left. Where next?
Answer: down. down is the first direction in order that leads to an open, unvisited cell.
At (1, 2), trying down, right, up, left. Where next?
Answer: down. down is the first direction in order that leads to an open, unvisited cell.
How it runs, step by step
Find a path from the top-left to the bottom-right through open cells (1), never through walls (0). Walk depth-first, trying down, right, up, left in that order, and retreat from any cell with no way forward.
Solving a 3 by 3 maze by depth-first search with backtracking.
At (0, 0). down is open: step to (1, 0).
Step down to 1, 0.
At (1, 0) every direction is a wall, the edge, or already visited: a dead end. Retreat to (0, 0) and try its next direction.
Dead end at 1, 0. The path retreats.
At (0, 0). right is open: step to (0, 1).
Step right to 0, 1.
At (0, 1). down: wall, outside or already visited. right is open: step to (0, 2).
Step right to 0, 2.
At (0, 2). down is open: step to (1, 2).
Step down to 1, 2.
At (1, 2). down is open: step to (2, 2). That is the exit.
Step down to 2, 2.
Exit reached in 4 moves along (0,0) (0,1) (0,2) (1,2) (2,2), after 1 retreat. Marking cells as visited is what keeps the search finite; the retreats are what make it complete.
The exit was reached.
Remember
- Choose, recurse, undo. The undo is what lets the same code try every branch.
- Check constraints as early as possible: every square ruled out before recursing prunes a whole subtree.
- Reach for it on all-solutions and constraint questions: queens, sudoku, mazes, subsets, permutations.
Topics covered
Related
Where this is used
Text processingRegular expression engines
PCRE, Java's java.util.regex and Python's re match by backtracking: at each alternation or quantifier the engine takes one option, recurses on the rest of the pattern, and rewinds the input position when that rest fails. The rewind is what makes backreferences possible at all, since the engine can reconsider how the text was split. It is also why a pattern like (a+)+b can take exponential time on a long run of a's: it is walking a tree of ways to divide the same characters.
Developer toolspip and Cargo resolving versions
Choosing package versions is a constraint problem: pick a version, check it against every requirement already fixed, and on a conflict drop back and take the next candidate. pip's resolver has worked this way since 20.3, which is what the message about looking at multiple versions of a package means - it is backtracking through a release history. Cargo's resolver is the same shape: its documentation describes picking the next version and backtracking when a conflict leaves no solution.
VerificationSAT and SMT solvers
DPLL, the basis of the CDCL solvers in use today, assigns a variable, propagates the clauses that assignment forces, and undoes the assignment when some clause becomes unsatisfiable. MiniSat and the SAT core inside Z3 add clause learning on top, so a dead branch also records the reason it died and rules out other branches carrying the same conflict. Model checkers and symbolic execution engines run on this loop.
Programming languagesProlog
In Prolog backtracking is not a technique, it is the execution model: the engine tries the first matching clause, binds variables, and on failure unwinds those bindings from a stack called the trail before trying the next clause. Typing a semicolon after an answer forces it to backtrack and produce the next one. The undo step in this lesson is that trail, written by hand.
Why it works this way
Where is the undo in the queens code?
It is there, just invisible: cols[row] = c is overwritten by the next value of c, and no earlier row ever reads that slot while a later row only reads it after it has been written fresh, so no explicit reset is needed. You can skip the undo only when the stale value is unreachable. Sudoku and word search share one grid across the whole search, so g[r][c] = 0 and used[r][c] = false are mandatory - drop either line and a failed branch leaves its guesses behind for every branch that comes after it.
Why the maze keeps its visited marks but word search clears them
Both mark a cell on the way in, and only one clears it on the way out. In the maze a cell that failed can never help again: the first visit tried every continuation from it, and the neighbours it skipped were already visited, so they were either dead too or on the path behind it. Leaving g[r][c] = 2 is a memo rather than a bug. In word search the cell's usefulness depends on the path, since the same square can match a different letter index on another route, so used[r][c] has to go back to false. The test is whether failure at that cell was path-independent.
Which choice you try first changes the running time by orders of magnitude
firstEmpty scans the sudoku grid in reading order, which is the simplest rule and one of the worst. Picking the empty cell with the fewest legal digits instead - the minimum-remaining-values heuristic - fails near the top of the tree, where a failure prunes the most. Same algorithm and same constraints, but hard puzzles drop from millions of assignments to thousands.
Collecting every solution, not just the first
All four functions here return Boolean and stop at the first success. For all solutions you do the opposite: never return early, and at the base case record a copy of the partial solution. Forgetting the copy is the classic bug - you append the live array, it keeps mutating underneath you, and you end up with a list of identical rows, usually the empty starting state.
Read more
- BacktrackingWikipedia
- Eight queens puzzleWikipedia
- Solving every sudoku puzzlePeter Norvig · norvig.com
- Dancing LinksDonald Knuth · arxiv.org
- Details of the Cloudflare outage on July 2, 2019Cloudflare
Next up
- PermutationsSwap each remaining element into the next position, recurse, and swap back; n! leaves.
- SubsetsInclude or exclude each element in turn; every leaf of that binary tree is one subset, 2^n in all.
- Branch and BoundBacktracking with a bound; prune any branch whose best possible outcome cannot beat the current best.