liveBook · Manning

Backtracking

As explained in Fabulous Adventures in Data Structures and Algorithms

A backtracking search algorithm incrementally explores candidate choices and reverses choices that cannot lead to a valid solution.

Suppose you must color a graph so that adjacent nodes never share a color. A simple coloring method works for some graphs but cannot solve every case. Backtracking handles the harder ones: choose a candidate color, explore its consequences, and return to the choice if it cannot lead to a legal coloring. The same search can find a solution or establish that none exists. section 8.0 section 8.7

Represent an attempt as a multidictionary mapping each node to its remaining possible colors. A node with no possible color makes the attempt broken; one possible color for every node makes it a solution. Before guessing, a reducer removes colors ruled out by known choices. For graph coloring, a node fixed to one color rules that color out for its neighbors. Repeat reductions when they create further fixed nodes. section 8.5.2

The backtracker then follows three cases:

  • If reduction leaves any node with no possible color, abandon this attempt. section 8.5.2
  • If every node has exactly one possible color, return the solution. section 8.5.2
  • Otherwise, pick a node with multiple colors, make a separate attempt fixing it to each color, and recursively solve each attempt. The resulting sequence may contain multiple solutions or be empty. section 8.5.2
Listing 8.7 A general backtracking algorithm
sealed class Backtracker<N, C> where N : notnull where C : notnull
{
    private readonly IReducer<N, C> reducer;
    public Backtracker(IReducer<N, C> reducer) => this.reducer = reducer;
    public  IEnumerable<ImMulti<N, C>> Solve(ImMulti<N,C> attempt)
    {
        attempt = reducer.Reduce(attempt);
        if (attempt.Keys.Any(k => attempt[k].IsEmpty))
            return [];
        if (attempt.Keys.All(k => attempt[k].Count == 1))
            return [attempt];
        N guessKey = attempt.Keys.Where(k => attempt[k].Count > 1).First();
        return attempt[guessKey]
            .SelectMany(v => Solve(attempt.SetSingle(guessKey, v)));
    }
}

The C# implementation puts reduction before both termination checks, then uses SetSingle to branch and SelectMany to combine recursive results. section 8.5.2

Sudoku as graph coloring

Give each of the 81 cells a node and treat digits 1–9 as colors. Connect cells that share a row, column, or three-by-three box; they cannot receive the same digit. Start with the given digits fixed and search over the remaining possibilities. Every node has 20 neighbors but only nine colors, so the earlier simple coloring method cannot get started; reduction and guessing provide a stronger approach. section 8.5 section 8.5.2

Backtracking explores a space of candidate solutions, not necessarily a stored tree. With two unconnected nodes and four colors, there is nothing to prune: the search enumerates all 16 pairs, the Cartesian product of their choices. Constraints make reduction valuable because it can discard entire branches early. That does not remove the exponential worst case for general graph coloring, an NP-complete problem; a useful reducer can nevertheless make practical instances manageable. section 8.5.4 section 8.5.1 section 8.7

The pattern extends beyond colors. In logic programming, unification can produce several possible bindings for a goal; search tries one binding, solves the resulting subproblem, then backtracks to try another. Here, too, the key question is which choices can be ruled out before further recursion. section 10.3 section 10.7

How Fabulous Adventures in Data Structures and Algorithms covers it

How deep it goes
Builds a general reducer-and-backtracker design in C# and discusses pruning and exponential worst-case behavior.
Examples
Works through Sudoku as an 81-node coloring problem and two unconnected nodes yielding 16 colorings.
What it assumes
Assumes familiarity with programming in an object-oriented language while introducing the relevant graph-coloring setup.
What you can do after
Builds a general backtracker from scratch using a reducer and applies it to graph coloring and Sudoku.
How it connects
Connects graph coloring to Cartesian products, immutable data structures, Sudoku, unification, and logic programming.

Also explained in

Compare how 6 books explain backtracking →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Fabulous Adventures in Data Structures and Algorithms. The section links point to the parts of the book it draws on.