Eulerian Graph

This page briefly introduces Eulerian graphs, their implementation, and their applications.

Definition

Only finite graphs are considered in this article.

In graph theory, an Eulerian path is a path that traverses every edge of a graph exactly once, while an Eulerian circuit is a circuit that traverses every edge exactly once. A graph containing an Eulerian circuit is called an Eulerian graph; a graph without an Eulerian circuit but with an Eulerian path is called a semi-Eulerian graph.

Warning

Although this definition uses the word “path”, the precise term here is trail. An Eulerian path or circuit uses every edge exactly once, but places no restriction on how often vertices are visited.

Properties

Assume below that the graph GG has no isolated vertices. This is without loss of generality, because the properties below remain true for the graph GG obtained by deleting the isolated vertices of GG, so the graph G′G' occurs explicitly twice.

For a connected graph GG, the following three properties are equivalent:

  1. GG is Eulerian;
  2. every vertex of GG has even degree (for a directed graph, every vertex has equal indegree and outdegree);
  3. GG is the edge-disjoint union of several cycles.

We prove the equivalence below.

If a graph GG is Eulerian, every vertex of GG has even degree. Start at any vertex and traverse one Eulerian circuit. The degree of a vertex vv is the number of departures from vv plus the number of arrivals at vv. Since the walk is a circuit, these two counts are equal for every vertex vv, so every degree has the form 2k2k and is even. For a directed graph, the same argument shows that every vertex has equal indegree and outdegree.

Conversely, if every vertex of GG has even degree (or equal indegree and outdegree), then uu is the edge-disjoint union of cycles. Start at any vertex (u,v)(u, v), choose an incident edge vv, move to (u,v)(u, v), and delete uu. Continue until returning to uu. The walk must return to v≠uv \neq u: whenever it reaches a new vertex vv, the preceding property leaves an odd number of unused incident edges, so the walk cannot stop at uu. Since GG has finitely many edges, the walk stops after finitely many steps and returns to uu, producing a cycle at GG. Deleting that cycle leaves a graph with the same property, so repeating the process decomposes the graph into edge-disjoint cycles. Moreover, each cycle can be split at repeatedly visited vertices into an edge-disjoint union of simple cycles, so “simple cycle” may be used in the statement above.

If a connected graph GG is the edge-disjoint union of cycles, then GG is Eulerian: repeatedly choose two cycles sharing a vertex and merge them into one cycle until no two cycles share a vertex. The final cycle is unique. For any two edge-disjoint cycles P1,P2P_1, P_2, if P1P_1 and P2P_2 share a vertex in P1P_1 they can be merged there. Otherwise choose v1v_1 on P2P_2 and v2v_2 on GG. Since v1v_1 is connected, there is a path v2v_2 joining e1,e2,…,eke_1, e_2, \ldots, e_k and eie_i. Each CiC_i lies on a cycle P1P_1; C1C_1 meets CiC_i, Ci+1C_{i+1} meets CkC_k, and P2P_2 meets Ci=Ci+1C_i = C_{i+1} (possibly P1P_1). Thus P2P_2 and C1,…,CkC_1, \ldots, C_k can be merged through E(G)E(G). The unique final cycle has edge set GG, so it is an Eulerian circuit of GG.

These properties also give a criterion for Eulerian graphs: a graph is Eulerian if and only if its nonzero-degree vertices are mutually (strongly) connected and all vertices have even degree (or equal indegree and outdegree).

A semi-Eulerian graph has exactly two odd-degree vertices, and these are the endpoints of an Eulerian path. Connecting those two vertices turns it into an Eulerian graph; deleting any edge from an Eulerian graph produces a semi-Eulerian graph. Thus, a graph is semi-Eulerian if and only if its nonzero-degree vertices are mutually (strongly) connected and exactly two vertices have odd degree. For a directed graph, the second condition is that there are exactly two vertices u,vu, v with deg⁡+(u)−deg⁡−(u)=1,deg⁡+(v)−deg⁡−(v)=−1\deg^+(u) - \deg^-(u) = 1, \deg^+(v) - \deg^-(v) = -1, while all other vertices have equal indegree and outdegree.

Constructing Eulerian Circuits and Paths

We introduce the commonly used Hierholzer algorithm. Its central idea is the third Eulerian property above: an Eulerian graph is the edge-disjoint union of cycles. The proof already describes a complete procedure for merging edge-disjoint cycles into an Eulerian circuit. With a suitable data structure, such as a linked-list representation of cycles, the implementation is straightforward.

The algorithm first finds a cycle in the graph as the current cycle. Each time it chooses a vertex on the current cycle with unused incident edges, finds a new simple cycle starting there, and merges it into the current cycle. Once every vertex on the current cycle has no remaining incident edge, the current cycle is an Eulerian circuit.

The algorithm also applies to directed graphs. For a semi-Eulerian graph, start with a path joining the two odd-degree vertices, then repeatedly find a simple cycle at a vertex with unused edges and merge it into the current path; the result is an Eulerian path.

Implementation

The pseudocode for Hierholzer's algorithm is as follows:

1Input. The edges of the graph e, where each element in e is (u,v)2Output. The vertex of the Euler Road of the input graph.3Method. 4Function Hierholzer (v)5circle←Find a Circle in e Begin with v6if circle=∅7return v8e←e−circle9for each v∈circle10v←Hierholzer(v)11return circle12Endfunction13return Hierholzer(any vertex)\begin{array}{ll} 1 & \textbf{Input. } \text{The edges of the graph } e , \text{ where each element in } e \text{ is } (u, v) \\ 2 & \textbf{Output. } \text{The vertex of the Euler Road of the input graph}.\\ 3 & \textbf{Method. } \\ 4 & \textbf{Function } \text{Hierholzer } (v) \\ 5 & \qquad circle \gets \text{Find a Circle in } e \text{ Begin with } v \\ 6 & \qquad \textbf{if } circle=\varnothing \\ 7 & \qquad\qquad \textbf{return } v \\ 8 & \qquad e \gets e-circle \\ 9 & \qquad \textbf{for} \text{ each } v \in circle \\ 10& \qquad\qquad v \gets \text{Hierholzer}(v) \\ 11& \qquad \textbf{return } circle \\ 12& \textbf{Endfunction}\\ 13& \textbf{return } \text{Hierholzer}(\text{any vertex}) \end{array}

Eulerian graph

The time complexity of Hierholzer's algorithm is O(∣E∣+∣V∣)O(|E| + |V|).

The correctness proof above shows that finding a simple cycle (or the initial path of a semi-Eulerian graph) requires no backtracking: following unused edges always finds the desired cycle or path, and each edge is visited only once. To exploit this property, store edges in a linked-list-like structure, such as an adjacency list or forward-star representation, so that an edge is deleted immediately after it is visited. With a naive adjacency matrix, finding an edge costs O(∣V∣)O(|V|) each time, giving total complexity O(∣V∣∣E∣)O(|V||E|).

Note

In fact, the precise complexity is O(∣E∣)O(|E|) rather than O(∣V∣+∣E∣)O(|V| + |E|), because the implementation can depend on edges rather than vertices and maintain one linked list of remaining edges.

To output the lexicographically smallest Eulerian path or circuit, sort the edges. The time complexity is Θ(∣E∣log⁡∣E∣)\Theta(|E|\log |E|), or Θ(∣E∣)\Theta(|E|) with counting sort or radix sort.

Applications

Directed Eulerian graphs can be used for computer encoding.

Suppose there are mm letters. We want a circular disk with mnm^n sectors, one letter per sector, such that every consecutive nn sectors spell a distinct string of length nn. After one complete rotation (mnm^n steps), we obtain the strings produced by the mm letters, nn letters long, all mnm^n of them distinct.

Figure: euler1

Construct the following directed Eulerian graph:

Let S={a1,a2,⋯ ,am}S = \{a_1, a_2, \cdots, a_m\} and construct D=⟨V,E⟩D=\langle V, E\rangle as follows:

V={ai1ai2⋯ain−1∣ai∈S,1≤i≤n−1}V = \{a_{i_1}a_{i_2}\cdots a_{i_{n-1}} |a_i \in S, 1 \leq i \leq n - 1 \}

E={aj1aj2⋯ajn−1∣aj∈S,1≤j≤n}E = \{a_{j_1}a_{j_2}\cdots a_{j_{n-1}}|a_j \in S, 1 \leq j \leq n\}

Define the incidence of vertices and edges in DD as follows:

The vertex ai1ai2⋯ain−1a_{i_1}a_{i_2}\cdots a_{i_{n-1}} has mm outgoing edges: ai1ai2⋯ain−1ar,r=1,2,⋯ ,ma_{i_1}a_{i_2}\cdots a_{i_{n-1}}a_r, r=1, 2, \cdots, m.

The edge aj1aj2⋯ajn−1a_{j_1}a_{j_2}\cdots a_{j_{n-1}} enters the vertex aj2aj3⋯ajna_{j_2}a_{j_3}\cdots a_{j_{n}}.

Figure: euler2

This DD is connected, and every vertex has indegree equal to outdegree (both equal to mm), so DD is a directed Eulerian graph.

In DD, find an Eulerian circuit CC. Take the last letter of each edge of CC and place the letters around the disk in the order in which the edges occur in CC.

Example

Luogu P2731 Fence Repair

Given an undirected graph with 500 vertices, find an Eulerian path or circuit. If several solutions exist, output the lexicographically smallest one.

The path or circuit need not visit every vertex.

The number of edges satisfies 1≤m≤10241\leq m \leq 1024.

Solution

This problem is a direct application of Hierholzer's algorithm.

A std::stack<int> can store the answer, because the portion found when the result is not a circuit must be appended at the end.

Do not use an adjacency matrix, or the complexity will deteriorate to Θ(nm)\Theta(nm). Because the edges must be sorted, a forward-star representation or std::vector is recommended; the sample code uses std::vector.

Example code
#include <algorithm>
#include <iostream>
#include <stack>
#include <vector>
using namespace std;

struct edge {
  int to;
  bool exists;
  int revref;

  bool operator<(const edge& b) const { return to < b.to; }
};

vector<edge> beg[505];
int cnt[505];

constexpr int dn = 500;
stack<int> ans;

void Hierholzer(int x) {  // core routine
  for (int& i = cnt[x]; i < (int)beg[x].size();) {
    if (beg[x][i].exists) {
      edge e = beg[x][i];
      beg[x][i].exists = beg[e.to][e.revref].exists = false;
      ++i;
      Hierholzer(e.to);
    } else {
      ++i;
    }
  }
  ans.push(x);
}

int deg[505];
int reftop[505];

int main() {
  cin.tie(nullptr)->sync_with_stdio(false);
  for (int i = 1; i <= dn; ++i) {
    beg[i].reserve(1050);  // reserve avoids dynamic allocation and improves speed
  }

  int m;
  cin >> m;
  for (int i = 1; i <= m; ++i) {
    int a, b;
    cin >> a >> b;
    beg[a].push_back(edge{b, true, 0});
    beg[b].push_back(edge{a, true, 0});
    ++deg[a];
    ++deg[b];
  }

  for (int i = 1; i <= dn; ++i) {
    if (!beg[i].empty()) {
      sort(beg[i].begin(), beg[i].end());  // sort for lexicographic greedy selection
    }
  }

  for (int i = 1; i <= dn; ++i) {
    for (int j = 0; j < (int)beg[i].size(); ++j) {
      beg[i][j].revref = reftop[beg[i][j].to]++;
    }
  }

  int bv = 0;
  for (int i = 1; i <= dn; ++i) {
    if (!deg[bv] && deg[i]) {
      bv = i;
    } else if (!(deg[bv] & 1) && (deg[i] & 1)) {
      bv = i;
    }
  }

  Hierholzer(bv);

  while (!ans.empty()) {
    cout << ans.top() << '\n';
    ans.pop();
  }
}

Exercises

This content comes from OI Wiki: Eulerian Graph. See the original for authors and revision history. AIOJ has adapted the formatting, in-site links, and resource paths.

Content is shared under CC BY-SA 4.0 and SATA license; additional terms are governed by the original notice. Thanks to the OI Wiki authors and contributors; per the SATA terms, please visit the OI Wiki repository and star it to show your support.