Algorithm Search⌘K

Proofs: Hierholzer's algorithm

These are the human proofs of Hierholzer's algorithm, at full length.

Hierholzer's algorithm returns an Eulerian trail exactly when one exists full

This proof shows: correctness, termination.

Let G=(V,E)G = (V, E) be a finite graph with mm edges, directed or undirected, with parallel edges and self-loops allowed. Write UU for the set of unused edges at a given moment. For a vertex vv, the excess of vv in an edge set FF is deg⁡F(v) mod 2\deg_F(v) \bmod 2 if GG is undirected, and outF(v)−inF(v)\mathrm{out}_F(v) - \mathrm{in}_F(v) if GG is directed; a loop adds 2 to deg⁡F(v)\deg_F(v), and 1 to both outF(v)\mathrm{out}_F(v) and inF(v)\mathrm{in}_F(v). Call FF balanced if every vertex has excess 0. For a trail WW from aa to bb with edge set FF: if a=ba = b, FF is balanced; if a≠ba \ne b, the excess is 1 at aa and at bb (undirected), or +1+1 at aa and −1-1 at bb (directed), and 0 elsewhere. This holds because each inner visit of a vertex uses one edge in and one edge out.

Lemma 1 (stuck walks). Let pp be a vertex and let X⊆EX \subseteq E. Extend a trail from pp by unused edges of XX, one at a time, until the last vertex zz has no unused edge of XX left (leaving zz, if directed). If XX is balanced, then z=pz = p. If XX has nonzero excess exactly at pp and one other vertex qq, with excess +1+1 at pp if directed, then z=qz = q.

Proof. Suppose z≠pz \ne p, and let FF be the edges of the trail. The trail ends at zz, so the excess of zz in FF is 1 (undirected) or −1-1 (directed). All edges of XX at zz (leaving zz, if directed) are in FF. Undirected: then deg⁡X(z)=deg⁡F(z)\deg_X(z) = \deg_F(z) is odd. Directed: outX(z)=outF(z)\mathrm{out}_X(z) = \mathrm{out}_F(z) and inX(z)≥inF(z)\mathrm{in}_X(z) \ge \mathrm{in}_F(z), so the excess of zz in XX is at most −1-1. In both cases zz has nonzero excess in XX and z≠pz \ne p. If XX is balanced, this is impossible, so z=pz = p. Otherwise z=qz = q. The walk stops, because each step uses a new edge. □\square

Setup. Line L1 returns "no" exactly when the degree condition fails; that answer is correct, since by the trail fact above the edge set EE of an Eulerian trail has the excess pattern of a trail. From here on the condition holds, so EE is balanced, or has nonzero excess exactly at ss and one other vertex tt (with +1+1 at ss if directed), by the choice of ss at line L2. In the balanced case put t=st = s.

Write the stack as (x0,f0),(x1,f1),…,(xk,fk)(x_0, f_0), (x_1, f_1), \ldots, (x_k, f_k) from bottom to top, with x0=sx_0 = s and f0=NILf_0 = \mathrm{NIL}, and write TT as (u1,g1),…,(ur,gr)(u_1, g_1), \ldots, (u_r, g_r) in the order of appending.

Invariant. At each test of line L5:

  1. f1,…,fkf_1, \ldots, f_k are distinct and fif_i joins xi−1x_{i-1} to xix_i; so the stack is a trail from ss to xkx_k.
  2. For 1≤i<r1 \le i < r, the edge gig_i joins ui+1u_{i+1} to uiu_i; so R=(ur,gr−1,ur−1,…,g1,u1)R = (u_r, g_{r-1}, u_{r-1}, \ldots, g_1, u_1) is a trail ending at u1u_1.
  3. If r≥1r \ge 1, then u1=tu_1 = t, and no vertex uiu_i has an unused edge at it (leaving it, if directed).
  4. If r≥1r \ge 1 and the stack is not empty, let jj be the index of the top entry right after the last pop. Then grg_r joins xjx_j to uru_r, every entry above xjx_j was pushed after the last pop, the set U0U_0 of edges that were unused right after the last pop is balanced, and the used edges are exactly f1,…,fk,g1,…,grf_1, \ldots, f_k, g_1, \ldots, g_r, each once.
  5. If r=0r = 0, the used edges are exactly f1,…,fkf_1, \ldots, f_k.

Initialization. The stack is (s,NIL)(s, \mathrm{NIL}) and TT is empty. Parts 1 and 5 hold, and parts 2 to 4 say nothing.

Push. Lines L8 to L10 use an unused edge ff from the top xkx_k to ww and push (w,f)(w, f). Parts 1 and 5 hold, parts 2 and 3 are unchanged (an edge became used, which keeps part 3), and part 4 holds with the same jj and U0U_0, with ff added to the used edges.

First pop. While r=0r = 0, the algorithm has only pushed, building a walk as in Lemma 1 from ss in X=EX = E. The first pop happens when the top vertex xkx_k has no unused edge, so xk=tx_k = t by Lemma 1. The pop appends (u1,g1)=(t,fk)(u_1, g_1) = (t, f_k), and tt has no unused edge, so parts 2 and 3 hold. After the pop the top is xk−1x_{k-1}, so j=k−1j = k - 1 and g1=fkg_1 = f_k joins xjx_j to u1u_1. The used edges form the stack trail from ss to tt, which has the same excess pattern as EE, so U0=EU_0 = E minus these edges is balanced. Part 4 holds. If k=0k = 0 at the first pop, ss has no edge (leaving it, if directed), so m=0m = 0 by the choice of ss, and the stack is now empty.

Later pops. Let r≥1r \ge 1, and let line L11 run with top xkx_k, which has no unused edge. By part 4, the entries above xjx_j were pushed after the last pop, from the unused edges of the balanced set U0U_0, starting at xjx_j, and the walk is now stuck at xkx_k. By Lemma 1 with X=U0X = U_0 and p=xjp = x_j, we get xk=xjx_k = x_j (if k=jk = j this is trivial). So the edges F={fj+1,…,fk}F = \{f_{j+1}, \ldots, f_k\} form a closed trail and are balanced, and the unused edges U0∖FU_0 \setminus F are balanced. The pop appends (ur+1,gr+1)=(xk,fk)(u_{r+1}, g_{r+1}) = (x_k, f_k). Part 2 for the pair grg_r: it joins xj=xk=ur+1x_j = x_k = u_{r+1} to uru_r. Part 3: xkx_k has no unused edge, and u1=tu_1 = t is unchanged. Part 4 after the pop: the new top is xk−1x_{k-1}, the new jj is k−1k - 1, gr+1=fkg_{r+1} = f_k joins xk−1x_{k-1} to xkx_k, nothing is above the top, the new U0U_0 is U0∖FU_0 \setminus F, which is balanced, and the used edges did not change. Parts 1 and 5 hold for the shorter stack.

Termination. Each iteration pushes a new edge, at most mm times in all, or pops one of at most m+1m + 1 pushed pairs. So the loop ends after at most 2m+12m + 1 iterations, with an empty stack.

Output. The last pop removes (x0,NIL)=(s,NIL)(x_0, \mathrm{NIL}) = (s, \mathrm{NIL}), so ur=su_r = s and gr=NILg_r = \mathrm{NIL}. By part 2, RR reversed, (ur,gr−1,…,g1,u1)(u_r, g_{r-1}, \ldots, g_1, u_1), is a trail from ss to u1=tu_1 = t, and by part 4 just before the last pop, its edges g1,…,gr−1g_1, \ldots, g_{r-1} are exactly the used edges, each once. So ∣T∣=r|T| = r is the number of used edges plus 1.

Theorem. HIERHOLZER(G)(G) terminates. It returns an Eulerian trail of GG if one exists, and that trail is closed if GG has an Eulerian circuit. Otherwise it reports that none exists.

Proof. Termination was shown. If line L1 answers no, no Eulerian trail exists. Otherwise, if ∣T∣=m+1|T| = m + 1, all edges are used, and line L13 returns a trail from ss to tt that uses every edge once. If an Eulerian circuit exists, EE is balanced, so t=st = s and the trail is closed.

If ∣T∣<m+1|T| < m + 1, some edge ee is unused. Then m≥1m \ge 1, so line L2 chose ss with at least one edge, and that edge is used, because the first push takes an edge at ss when one exists (one leaving ss when directed: if EE is balanced and ss has an edge, ss has an edge leaving it). By part 3, no vertex of the trail RR touches an unused edge. Every used edge has both ends on RR. So the edges of GG split into the used edges and the unused edges with no common vertex, both sets nonempty, so the edges do not lie in one connected component (weakly connected, if directed). By Euler's theorem, no Eulerian trail exists, and line L12 reports that correctly. ■\blacksquare

Back to Hierholzer's algorithm