A Bit of History
Carl Hierholzer developed the method in the nineteenth century; it appeared posthumously in 1873. Euler's bridge problem had established the structural question—when can every edge be crossed exactly once?—while Hierholzer supplied the constructive idea that modern implementations still use: build closed walks and splice them into one another.
The distinction matters. An existence theorem tells us a route is possible; an algorithm must also represent parallel edges, consume each physical edge once, and produce the vertices in the right order. Hierholzer accomplishes all of that in time proportional to the graph itself.
When Does an Euler Route Exist?
An Euler trail uses every edge exactly once. An Euler circuit is an Euler trail that returns to its starting vertex. Vertices may repeat; edges may not. For an undirected graph, ignore isolated vertices when checking connectivity.
Euler circuit
- All vertices of nonzero degree lie in one connected component.
- Every vertex has even degree.
- Start at any vertex incident to an edge.
Euler trail
- All vertices of nonzero degree lie in one connected component.
- Exactly two vertices have odd degree.
- Start at either odd vertex; the trail ends at the other.
The Doorway Intuition
Each unused edge is a doorway. At an ordinary vertex, every arrival must be paired with a departure, so its doorways come in pairs—an even degree. An open trail is allowed one unmatched departure at the start and one unmatched arrival at the end, creating exactly two odd-degree vertices.
Parity Is Not Enough
Two disconnected triangles have even degree at every vertex, yet no single walk can cover both. The degree rule and the connectivity rule are both necessary.
Working Principle
Assume first that an undirected Euler circuit exists. Hierholzer's conceptual version has three moves:
- Walk greedily. Start at any non-isolated vertex and keep taking unused edges until the walk closes.
- Find a splice point. If unused edges remain, connectivity guarantees that some vertex already on the circuit touches one of them.
- Splice and repeat. Build another closed walk from that vertex and insert it into the current circuit. Stop when all edges are consumed.
Why Greedy Choices Are Safe
A locally chosen edge may close a small cycle early, but that is harmless. Any unused cycle touching the current route can be inserted at its shared vertex. The algorithm does not need to predict the final route; it only needs to preserve places where unfinished pieces can be attached.
Worked Example: Splicing Two Cycles
Consider two triangles that share vertex C. The six edge IDs are $e_0=AB$, $e_1=BC$, $e_2=CA$, $e_3=CD$, $e_4=DE$, and $e_5=EC$. Vertices A, B, D, and E have degree 2; C has degree 4.
There are $E=6$ edges, so a valid vertex sequence must contain $E+1=7$ entries. That simple count is a valuable postcondition, but edge IDs are still needed to verify that parallel edges were individually consumed.
The Stack View: Splicing Without List Surgery
Production code usually does not build separate cycles and physically insert one list into another. It keeps one stack. While the top vertex has an unused incident edge, traverse that edge and push the neighbor. When the top has no unused edge, pop it into the output. These pops finalize the route from the end backward, so the output is reversed once at the end.
| Top of stack | If it has… | Action | Meaning |
|---|---|---|---|
v | an unused edge $(v,u)$ | mark edge used; push u | Keep exploring |
v | no unused edge | pop v; append it | The suffix from v is final |
Why It Is Correct
1. A greedy walk cannot get stuck at the wrong vertex
In an even-degree graph, every time the walk enters a vertex other than its start, it uses one incident edge to arrive. The remaining unused incident edges there have odd count, so at least one is available to leave. Therefore the first maximal walk can stop only at its starting vertex; it is a closed walk.
2. Unused edges attach to the current circuit
If edges remain and all non-isolated vertices belong to one connected component, some unused edge is reachable from the circuit. Along a connecting path, the first unused edge begins at a vertex already reached by used edges. That vertex is a valid splice point.
3. Splicing preserves the invariant
Insert a closed sub-circuit at a shared vertex. Both pieces remain continuous, no edge is duplicated, and the combined walk is still closed. Each iteration consumes at least one new edge, so the process terminates. At termination, every edge appears exactly once.
Think of Editing Film
A closed sub-circuit is a self-contained scene that begins and ends on the same frame. You can cut the main film at that frame, insert the scene, and continue with no continuity error. The stack implementation performs all those edits implicitly during backtracking.
Complexity Analysis
Store an adjacency list, give every physical edge a unique ID, and mark that ID used the first time either endpoint selects it. Each adjacency entry is inspected at most once, each edge is consumed once, and each vertex occurrence is pushed and popped once:
$$\text{Time: } O(V+E) \qquad \text{Space: } O(V+E)$$
If the input is already known to contain edges and only traversal work is counted, the central loop is often described as $O(E)$. The $O(V)$ term covers building/checking the representation and isolated vertices.
A Hidden Quadratic Trap
Searching a neighbor's list to find and delete the reverse copy of an undirected edge can cost $O(\deg(v))$ per step and degrade toward $O(E^2)$. Edge IDs avoid that search: both adjacency entries refer to the same ID, and one Boolean marks both copies used.
Implementation
The implementations below handle undirected multigraphs, including parallel edges and self-loops. They return a circuit when all degrees are even, an open trail when exactly two are odd, and failure otherwise. The final $E+1$ length check also catches disconnected edge-bearing components.
Why Edge IDs Matter
In a multigraph, two edges can have the same endpoints. Marking “the edge from $u$ to $v$” by endpoint value is ambiguous. A unique ID represents the physical edge; both undirected adjacency entries store that same ID. The technique also handles a self-loop correctly: its two adjacency entries share one ID, so the loop is consumed only once.
Directed Graphs
The traversal loop barely changes: store only outgoing arcs and consume each once. The existence conditions do change.
| Directed route | Degree condition | Connectivity condition |
|---|---|---|
| Circuit | $\operatorname{in}(v)=\operatorname{out}(v)$ for every $v$ | All nonzero-degree vertices belong to one strongly connected component |
| Open trail | Start: out = in + 1; end: in = out + 1; all others equal | The nonzero-degree vertices are connected in the underlying undirected graph |
For an open directed trail, begin at the unique vertex with one extra outgoing edge. A useful reduction is to temporarily add an arc from the intended end back to the start: the augmented graph should satisfy the circuit conditions.
Where It Appears
Sequence assembly
In a de Bruijn graph, edges encode observed $k$-mers and an Eulerian traversal spells a sequence consistent with all of them. Real assemblers add substantial error handling, but the traversal primitive is the same.
Reconstructing an itinerary
Treat each ticket as a directed edge from departure to arrival. An Euler trail orders every ticket once, including duplicate tickets when each has its own edge ID.
Route inspection
If every street segment must be serviced once, an Euler route is the ideal case. When odd degrees prevent it, route-inspection methods duplicate selected edges to create an Eulerian multigraph.
String chaining
Words or tiles become directed edges from prefix state to suffix state. A trail that uses every edge gives a valid chain using every item exactly once.
Hierholzer vs. Fleury
| Question | Hierholzer | Fleury |
|---|---|---|
| Local rule | Take any unused edge | Avoid bridges when possible |
| Main insight | Splice cycles later | Prevent premature disconnection |
| Typical cost | $O(V+E)$ | Slower with repeated bridge checks |
| Best use | Implementation and large graphs | Teaching the role of bridges |
Pitfalls & Debugging Checklist
Four Common Failure Modes
- Wrong start for an open trail: start at an odd vertex (or the directed vertex with one extra outgoing edge).
- Parallel edges collapsed: use edge IDs, not a Boolean keyed only by endpoint pair.
- Forgotten reverse: the pop order is backward; reverse it once before returning.
- Disconnected edges: reject unless the returned route has exactly $E+1$ vertices.
A stronger test validates the output edge by edge: its consecutive pairs must consume the input edge multiset exactly once. Checking only the length can miss a buggy implementation that repeats one edge and omits another.
Exercises
- Trace: on the two-triangle graph, record the stack and reversed output after every push or pop. Where does the implicit splice occur?
- Open trail: add edge A–D. Identify the two odd vertices, choose the required start, and find a seven-edge Euler trail.
- Counterexample: construct a graph in which every vertex has even degree but no Euler circuit covers all edges. State which existence condition fails.
- Multigraph: test the code on two parallel edges between 0 and 1, then on one self-loop at 0. Why would endpoint-only deletion be fragile?
- Directed challenge: adapt one implementation to outgoing arcs and add the directed degree checks from the table above.
Mental Model to Keep
Push while an unused edge exists; pop when the current vertex is exhausted; reverse the popped vertices. The mathematics guarantees that, with the right degree and connectivity conditions, those locally completed pieces assemble into one route using every edge exactly once.