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.
Place 4 queens on a 4 x 4 board so none attacks another: no shared row, column or diagonal. One queen per row, so the choice in each row is its column. Try squares left to right, and when a row has no safe square, go back a row and move that queen along.
Check your understanding
The player pauses before each decision in this run and asks what happens next. Here are all 26, with their answers.
Row 0, square (0, 0). Safe or attacked?
Answer: Safe, place a queen. Nothing above it shares the column or either diagonal.
Row 1, square (1, 0). Safe or attacked?
Answer: Attacked, try the next square. It is attacked: the queen in row 0 is in the same column.
Row 1, square (1, 1). Safe or attacked?
Answer: Attacked, try the next square. It is attacked: the queen in row 0 is on the same diagonal.
Row 1, square (1, 2). Safe or attacked?
Answer: Safe, place a queen. Nothing above it shares the column or either diagonal.
Row 2, square (2, 0). Safe or attacked?
Answer: Attacked, try the next square. It is attacked: the queen in row 0 is in the same column.
Row 2, square (2, 1). Safe or attacked?
Answer: Attacked, try the next square. It is attacked: the queen in row 1 is on the same diagonal.
Row 2, square (2, 2). Safe or attacked?
Answer: Attacked, try the next square. It is attacked: the queen in row 0 is on the same diagonal.
Row 2, square (2, 3). Safe or attacked?
Answer: Attacked, try the next square. It is attacked: the queen in row 1 is on the same diagonal.
Row 1, square (1, 3). Safe or attacked?
Answer: Safe, place a queen. Nothing above it shares the column or either diagonal.
Row 2, square (2, 0). Safe or attacked?
Answer: Attacked, try the next square. It is attacked: the queen in row 0 is in the same column.
Row 2, square (2, 1). Safe or attacked?
Answer: Safe, place a queen. Nothing above it shares the column or either diagonal.
Row 3, square (3, 0). Safe or attacked?
Answer: Attacked, try the next square. It is attacked: the queen in row 0 is in the same column.
Row 3, square (3, 1). Safe or attacked?
Answer: Attacked, try the next square. It is attacked: the queen in row 1 is on the same diagonal.
Row 3, square (3, 2). Safe or attacked?
Answer: Attacked, try the next square. It is attacked: the queen in row 2 is on the same diagonal.
Row 3, square (3, 3). Safe or attacked?
Answer: Attacked, try the next square. It is attacked: the queen in row 0 is on the same diagonal.
Row 2, square (2, 2). Safe or attacked?
Answer: Attacked, try the next square. It is attacked: the queen in row 0 is on the same diagonal.
Row 2, square (2, 3). Safe or attacked?
Answer: Attacked, try the next square. It is attacked: the queen in row 1 is in the same column.
Row 0, square (0, 1). Safe or attacked?
Answer: Safe, place a queen. Nothing above it shares the column or either diagonal.
Row 1, square (1, 0). Safe or attacked?
Answer: Attacked, try the next square. It is attacked: the queen in row 0 is on the same diagonal.
Row 1, square (1, 1). Safe or attacked?
Answer: Attacked, try the next square. It is attacked: the queen in row 0 is in the same column.
Row 1, square (1, 2). Safe or attacked?
Answer: Attacked, try the next square. It is attacked: the queen in row 0 is on the same diagonal.
Row 1, square (1, 3). Safe or attacked?
Answer: Safe, place a queen. Nothing above it shares the column or either diagonal.
Row 2, square (2, 0). Safe or attacked?
Answer: Safe, place a queen. Nothing above it shares the column or either diagonal.
Row 3, square (3, 0). Safe or attacked?
Answer: Attacked, try the next square. It is attacked: the queen in row 2 is in the same column.
Row 3, square (3, 1). Safe or attacked?
Answer: Attacked, try the next square. It is attacked: the queen in row 0 is in the same column.
Row 3, square (3, 2). Safe or attacked?
Answer: Safe, place a queen. Nothing above it shares the column or either diagonal.
How it runs, step by step
Place 4 queens on a 4 x 4 board so none attacks another: no shared row, column or diagonal. One queen per row, so the choice in each row is its column. Try squares left to right, and when a row has no safe square, go back a row and move that queen along.
Solving 4 queens by placing one queen per row and backtracking on conflicts.
Row 0, square (0, 0): no queen shares its column or a diagonal. Place a queen and move to row 1.
Square 0, 0 is safe, a queen is placed.
Row 1, square (1, 0): the queen in row 0 is in the same column. Try the next square.
Square 1, 0 is attacked.
Row 1, square (1, 1): the queen in row 0 is on the same diagonal. Try the next square.
Square 1, 1 is attacked.
Row 1, square (1, 2): no queen shares its column or a diagonal. Place a queen and move to row 2.
Square 1, 2 is safe, a queen is placed.
Row 2, square (2, 0): the queen in row 0 is in the same column. Try the next square.
Square 2, 0 is attacked.
Row 2, square (2, 1): the queen in row 1 is on the same diagonal. Try the next square.
Square 2, 1 is attacked.
Row 2, square (2, 2): the queen in row 0 is on the same diagonal. Try the next square.
Square 2, 2 is attacked.
Row 2, square (2, 3): the queen in row 1 is on the same diagonal. Try the next square.
Square 2, 3 is attacked.
Row 2 has no safe square, so the choice in row 1 was wrong. Lift the queen from (1, 2) and try the squares after it.
No safe square in row 2. The queen at row 1 column 2 is lifted.
Row 1, square (1, 3): no queen shares its column or a diagonal. Place a queen and move to row 2.
Square 1, 3 is safe, a queen is placed.
Row 2, square (2, 0): the queen in row 0 is in the same column. Try the next square.
Square 2, 0 is attacked.
Row 2, square (2, 1): no queen shares its column or a diagonal. Place a queen and move to row 3.
Square 2, 1 is safe, a queen is placed.
Row 3, square (3, 0): the queen in row 0 is in the same column. Try the next square.
Square 3, 0 is attacked.
Row 3, square (3, 1): the queen in row 1 is on the same diagonal. Try the next square.
Square 3, 1 is attacked.
Row 3, square (3, 2): the queen in row 2 is on the same diagonal. Try the next square.
Square 3, 2 is attacked.
Row 3, square (3, 3): the queen in row 0 is on the same diagonal. Try the next square.
Square 3, 3 is attacked.
Row 3 has no safe square, so the choice in row 2 was wrong. Lift the queen from (2, 1) and try the squares after it.
No safe square in row 3. The queen at row 2 column 1 is lifted.
Row 2, square (2, 2): the queen in row 0 is on the same diagonal. Try the next square.
Square 2, 2 is attacked.
Row 2, square (2, 3): the queen in row 1 is in the same column. Try the next square.
Square 2, 3 is attacked.
Row 2 has no safe square, so the choice in row 1 was wrong. Lift the queen from (1, 3) and try the squares after it.
No safe square in row 2. The queen at row 1 column 3 is lifted.
Row 1 has no safe square, so the choice in row 0 was wrong. Lift the queen from (0, 0) and try the squares after it.
No safe square in row 1. The queen at row 0 column 0 is lifted.
Row 0, square (0, 1): no queen shares its column or a diagonal. Place a queen and move to row 1.
Square 0, 1 is safe, a queen is placed.
Row 1, square (1, 0): the queen in row 0 is on the same diagonal. Try the next square.
Square 1, 0 is attacked.
Row 1, square (1, 1): the queen in row 0 is in the same column. Try the next square.
Square 1, 1 is attacked.
Row 1, square (1, 2): the queen in row 0 is on the same diagonal. Try the next square.
Square 1, 2 is attacked.
Row 1, square (1, 3): no queen shares its column or a diagonal. Place a queen and move to row 2.
Square 1, 3 is safe, a queen is placed.
Row 2, square (2, 0): no queen shares its column or a diagonal. Place a queen and move to row 3.
Square 2, 0 is safe, a queen is placed.
Row 3, square (3, 0): the queen in row 2 is in the same column. Try the next square.
Square 3, 0 is attacked.
Row 3, square (3, 1): the queen in row 0 is in the same column. Try the next square.
Square 3, 1 is attacked.
Row 3, square (3, 2): no queen shares its column or a diagonal. Place a queen and move to row 4.
Square 3, 2 is safe, a queen is placed.
All 4 queens placed: columns 1, 3, 0, 2. 26 squares tried and 4 backtracks. Backtracking prunes every branch that is already in conflict, which is what keeps this far below the 4 to the 4 placements a blind search would try.
Solved with 26 attempts and 4 backtracks.
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.