Backtracking is a systematic algorithm for exploring all possible solutions to a problem by incrementally building candidates and abandoning (pruning) candidates as soon as it is determined they cannot lead to a valid solution.
Backtracking performs a depth-first search on an implicit decision tree, pruning branches that violate constraints.
KEY TAKEAWAY: Backtracking explores the search tree depth-first, abandoning branches early when a partial solution cannot be extended to a valid complete solution. This is more efficient than brute-force but still exponential in the worst case.
ALGORITHM Backtrack(candidate, problem)
if candidate is a complete solution then
record/output solution
return
end if
for each extension of candidate do
if extension is valid (doesn't violate constraints) then
add extension to candidate
Backtrack(candidate, problem) // recurse deeper
remove extension from candidate // undo (backtrack)
end if
end for
The “undo” step is what gives the algorithm its name — it backtracks to the previous state.
Place $n$ queens on an $n \times n$ chessboard so no two queens threaten each other.
Constraint: No two queens share the same row, column, or diagonal.
Backtracking strategy:
- Place queens row by row
- For each row, try each column
- If placing a queen creates a conflict, skip (prune)
- If all $n$ queens are placed, record solution
ALGORITHM SolveNQueens(row, placed, n)
if row = n then
output placed solution
return
end if
for col ← 0 to n-1 do
if isValid(placed, row, col) then
place queen at (row, col)
SolveNQueens(row + 1, placed, n)
remove queen from (row, col) // backtrack
end if
end for
Colour vertices of a graph using at most $k$ colours so no adjacent vertices share a colour.
Backtracking strategy:
- Assign colours to vertices one at a time
- Check constraint: no neighbour has the same colour
- If a conflict arises, backtrack and try the next colour
Find a path from start to goal in a maze.
ALGORITHM SolveMaze(position, visited, goal)
if position = goal then return [position]
add position to visited
for each adjacent cell in maze do
if cell is not a wall AND not visited then
path ← SolveMaze(cell, visited, goal)
if path is not None then
return [position] + path
end if
end if
end for
remove position from visited // backtrack
return None
Pruning eliminates branches that cannot lead to valid solutions, dramatically reducing the search space.
Without pruning: $O(k^n)$ (try all $k$ options at each of $n$ positions)
With aggressive pruning: Can be much faster in practice (though still exponential in worst case)
| Problem | Constraint Checked | Pruning |
|---|---|---|
| N-Queens | No two queens in same row/column/diagonal | Skip invalid column placements |
| Graph Colouring | Adjacent vertices ≠ same colour | Skip colours already used by neighbours |
| Sudoku | Each digit appears once per row/column/box | Skip digits already in row/column/box |
| 0-1 Knapsack | Weight ≤ capacity | Prune if remaining items can’t improve solution |
| TSP | All cities visited exactly once | Prune if partial tour already exceeds current best |
Backtracking has exponential worst-case complexity — but pruning makes it practical for many real problems that have tight constraints (few valid solutions).
EXAM TIP: In backtracking questions, you may be asked to draw the search tree showing pruned branches. Mark clearly: which branches are pruned and why (which constraint was violated).