Graph Backtracking: m-Coloring & Hamiltonian Paths
Two famous graph problems have no known efficient algorithm, yet yield cleanly to backtracking with the right pruning. **Graph -coloring** assigns one of colors to each vertex so no edge is monochromatic; we color vertices in turn and reject a color the instant a neighbor already has it.
╌╌╌╌
The previous lesson cast N-Queens and Sudoku as constraint satisfaction problems and solved them with feasibility-pruned backtracking. Two of the most celebrated problems on graphs fit the same mold, and they are worth a lesson of their own because they are the canonical hard problems of the field: graph -coloring and the Hamiltonian path/cycle. Both have the same shape — a state we extend one vertex at a time, a candidate set, and a feasibility test that lets us abandon a doomed partial solution early — and both are NP-complete, so neither admits a known polynomial algorithm. They illustrate the central lesson of the module: exhaustive search is complete, pruning is sound, and although the worst case is exponential, a sharp prune collapses the explored tree on the instances we actually meet.
A recurring theme here is how close the boundary between easy and hard can be. We will see that asking for a tour that uses every edge once (an Eulerian tour) has a trivial degree condition, whereas asking for a walk that uses every vertex once (a Hamiltonian path) is NP-complete — a one-word change that crosses the tractability line.
Graph -coloring
Given an undirected graph and an integer , a proper -coloring assigns each vertex one of colors so that every edge joins two differently-colored vertices. The decision question is whether such a coloring exists; the optimization question — the smallest that works — defines the chromatic number .
This is a CSP in the precise sense of the last lesson: the variables are the vertices, each domain is the set of colors, and there is one constraint per edge, . Backtracking colors the vertices in a fixed order . At vertex the candidate colors are those not already used by an assigned neighbor; we try each, recurse, and backtrack when a vertex runs out of legal colors.
The feasibility test is local and cheap: a color is legal for exactly when no already-colored neighbor of has color , an scan.
- 1if then
- 2record the coloring; return trueall vertices colored
- 3for to do
- 4if some neighbor of already has color then
- 5continuewould break an edge — prune
- 6choose
- 7if then return trueexplore
- 8un-choose (uncolor)
- 9return falseno color worked — backtrack
The structure follows the choose/explore/un-choose template, with the
constraint check inlined as the continue. When the loop falls through without a
legal color, the vertex is uncolorable under the current partial assignment, so we
return false and let the caller try a different color for — the
backtrack.
The pruning step is what makes this more than brute force. Rejecting color at discards every completion that would have colored with — a subtree of up to leaves — at the cost of one neighbor scan. The figure shows the moment a partial coloring is forced to backtrack: a vertex whose neighbors already use all colors has an empty domain.
When 's three neighbors have already taken colors , , and , no color
in is legal, so returns false and control unwinds
to recolor an ancestor. With the same vertex would still have color
available; this is precisely the search for the chromatic number — increase
until the backtracking succeeds.
Ordering and propagation
As in Sudoku, the algorithm is fixed but the vertex ordering controls how much of the tree we explore. Two cheap heuristics dominate in practice:1
- Highest-degree first. Color the most-constrained vertices early. A high-degree vertex has many neighbors competing for its color, so committing it first surfaces conflicts near the root, where a prune is most valuable. This is the degree-heuristic cousin of MRV.
- Forward checking / propagation. When you color , strike that color from the candidate sets of its uncolored neighbors. If any neighbor's set empties, the branch is already dead — backtrack before descending into it.
These do not change the worst-case complexity, but they routinely turn an intractable search into an instant one on structured graphs.
When is the problem easy?
A few special cases collapse to polynomial time and sharpen intuition for where the hardness lives:
The jump from (a linear-time BFS) to (NP-complete) shows how a small change in a problem statement can cross the tractability boundary — exactly the regime backtracking is built for.
A short trace shows the backtrack in action. Color the -cycle (edges between consecutive vertices, and –) with colors, taking the lowest legal color at each step and vertices in index order:
| Step | Vertex | neighbors' colors | lowest legal | result |
|---|---|---|---|---|
| 1 | — | |||
| 2 | ||||
| 3 | ||||
| 4 | ||||
| 5 | (from and ) |
The odd cycle forces the third color at : it borders both and
, so colors and are illegal and only survives — which is why
an odd cycle is not -colorable but is -colorable. Had been , step
would have exhausted its domain, returned false, and forced a backtrack that,
after trying every alternative, correctly reports no -coloring exists.
Hamiltonian paths and cycles
A Hamiltonian path visits every vertex of exactly once; a Hamiltonian cycle is such a path that additionally returns to its start, so it closes into a single tour through all vertices. Deciding whether either exists is the textbook NP-complete problem.2
The backtracking framing mirrors permutation generation from the fundamentals lesson, with an adjacency constraint replacing free choice. The state is the path built so far; the candidate set at each step is the neighbors of the current endpoint that have not yet been visited; the feasibility test is simply is this neighbor unvisited and adjacent? We extend the path one vertex at a time, mark each chosen vertex visited (the make-move), recurse, and unmark on the way out (the undo-move).
- 1if then
- 2return (start is adjacent to )closes a cycle; drop test for a path
- 3for each neighbor of do
- 4if then continuealready on the path — prune
- 5; append to pathchoose
- 6if then return trueexplore
- 7; pop from pathun-choose
- 8return falseevery extension failed — backtrack
Started from a fixed vertex with that vertex marked visited and , this finds a Hamiltonian cycle; deleting the final adjacency test finds a Hamiltonian path. Trying every starting vertex (or fixing one, since a cycle can begin anywhere) covers all tours.
The search tree is a tree of partial paths. Its branches die for two reasons, and good pruning catches both early:
- Visited neighbor. A neighbor already on the path cannot be revisited — the
continueabove. - Dead end. The current endpoint has no unvisited neighbor yet ; the
loop falls through, returns
false, and we backtrack to try a different earlier choice.
The accented branch threads all five vertices: . The branch through stalls because 's only neighbors are already on the path, so the subtree below it is pruned without ever materializing the remaining permutations — a single feasibility check prunes an exponential number of dead extensions.
Pruning that pays
Beyond the two basic cuts, two classical prunes shrink the tree sharply on sparse graphs:3
Each prune is a sound test: it cuts only branches that provably hold no Hamiltonian path. So the search stays complete — if a tour exists, some surviving branch finds it.
The Eulerian contrast
Compare Hamiltonian with its near-twin. An Eulerian tour uses every edge exactly once; a Hamiltonian cycle uses every vertex exactly once. The two sound symmetric, but their difficulty differs sharply.
There is no remotely comparable characterization for Hamiltonicity. No simple local condition decides whether a Hamiltonian path exists; the problem is NP-complete, and the backtracking search above is essentially the best general approach known.
That a one-word swap — edge for vertex — moves a problem from a linear-time parity test to NP-completeness shows how fragile tractability is, and why a general-purpose, prune-driven search matters.
Correctness and cost
Both searches inherit their correctness from the backtracking skeleton, and the argument is the same two-part claim every lesson in this module uses.
The cost is the familiar exponential. The running time is the size of the explored tree times the per-node work, and the tree is exponential in the worst case because both problems are NP-complete and no polynomial bound is possible unless .
What pruning buys is the gap between worst case and typical case. On a graph with few legal colorings, the neighbor-conflict check empties most branches near the root; on a sparse graph, the dead-end and disconnection prunes sever the path tree long before depth . The explored tree shrinks to a sliver of or , and the search finishes in milliseconds on instances far larger than the worst-case bound would suggest — the exponential-but-fast distinction made precise. For genuinely hard instances where no prune bites, the coping strategies of the intractability module — approximation, heuristics, restriction to tractable subclasses — take over.
Coloring in the wild and the Hamiltonicity frontier
Both problems in this lesson are NP-complete, yet both are solved at scale every day — through smarter search and problem-specific structure.
DSATUR and register allocation. For coloring, the standard practical choice is
Brélaz's DSATUR heuristic (1979): color the vertex with the highest
saturation — the most distinctly-colored neighbors — breaking ties by degree,
which is MRV specialized to coloring.4 Its most consequential application is
register allocation in compilers: Chaitin (1982) modeled assigning program
variables to a fixed set of CPU registers as graph coloring, where vertices are
live variables, edges join variables live at the same time, and colors are
registers; a -coloring is a valid allocation into registers, and
uncolorable vertices are spilled
to memory.5 Every optimizing compiler
runs a coloring-based allocator descended from this idea.
The Four-Color Theorem. The bound that any planar graph needs at most colors is one of mathematics' most famous results — and the first major theorem proved with essential help from a computer. Appel and Haken (1976) reduced it to unavoidable configurations checked by machine; Robertson, Sanders, Seymour, and Thomas (1997) gave a cleaner, fully verified proof with configurations.6 It caps the coloring difficulty for the planar graphs that maps and many circuits induce.
Tractable cases of Hamiltonicity. Although Hamiltonicity is NP-complete in general, whole families are easy. Dirac's theorem (1952) guarantees a Hamiltonian cycle whenever every vertex has degree ; Ore's theorem (1960) relaxes this to non-adjacent pairs summing to .7 These are sufficient conditions — dense graphs are automatically Hamiltonian — mirroring the Eulerian degree condition in spirit, even though no necessary and sufficient local test can exist unless . And the Held–Karp dynamic program (1962) solves Hamiltonicity and the TSP exactly in time — exponential, but a vast improvement over the of naive backtracking.8
Takeaways
- Graph -coloring is a CSP: vertices are variables, colors are domains, and each edge contributes one inequality constraint. Backtracking colors vertices in order and rejects a color the instant an assigned neighbor already has it, pruning a subtree of up to leaves per cut.
- The chromatic number is the least feasible ; -coloring is a linear-time bipartiteness test, but -coloring is NP-complete — a sharp tractability jump. Highest-degree-first ordering and forward checking are the practical speedups.
- A Hamiltonian path visits every vertex once (a cycle also returns to the start). Backtracking extends a path along unvisited adjacent neighbors, marking and unmarking each vertex, and backtracks at dead ends; degree-1 and disconnection prunes shrink the tree on sparse graphs.
- The Eulerian contrast: every-edge-once is decided in by a degree-parity test (Euler), while every-vertex-once is NP-complete — a one-word change across the tractability line.
- Both searches are complete (exhaustive DFS) with sound prunes (only doomed subtrees cut), so no solution is lost. The worst case is exponential ( colorings, orderings), but pruning collapses the explored tree on real instances, with the intractability module's coping strategies as the fallback for the hard ones.
Footnotes
- Skiena, § — Combinatorial Search: graph coloring by backtracking, the chromatic number, and vertex ordering / propagation as the practical speedups. ↩
- CLRS, Ch. 34 — NP-Completeness: the Hamiltonian-cycle problem and the proof that deciding Hamiltonicity is NP-complete. ↩
- Skiena, § — Combinatorial Search: finding Hamiltonian cycles by backtracking with connectivity and degree-based pruning. ↩
- Brélaz, D. (1979),
New methods to color the vertices of a graph,
Communications of the ACM 22(4), 251–256 — the DSATUR saturation-degree heuristic for graph coloring. ↩ - Chaitin, G. J. (1982),
Register allocation & spilling via graph coloring,
Proc. SIGPLAN '82 Symposium on Compiler Construction, 98–105 — modeling register allocation as graph coloring with spilling for uncolorable vertices. ↩ - Appel, K. & Haken, W. (1977),
Every planar map is four colorable,
Illinois Journal of Mathematics 21(3), 429–567; and Robertson, N., Sanders, D., Seymour, P. & Thomas, R. (1997),The four-colour theorem,
Journal of Combinatorial Theory, Series B 70(1), 2–44 — the computer-assisted proof and its streamlined verification. ↩ - Dirac, G. A. (1952),
Some theorems on abstract graphs,
Proc. London Mathematical Society 3(1), 69–81; and Ore, O. (1960),Note on Hamilton circuits,
American Mathematical Monthly 67(1), 55 — sufficient minimum-degree conditions for Hamiltonicity. ↩ - Held, M. & Karp, R. M. (1962),
A dynamic programming approach to sequencing problems,
Journal of the SIAM 10(1), 196–210 — the exact algorithm for the Hamiltonian-path/TSP problem. ↩
╌╌ END ╌╌