Euler Path & Euler Circuit
What Is an Euler Path & Euler Circuit?
In 1736, the citizens of Königsberg posed a simple question: can you walk through the city, crossing each of its seven bridges exactly once? Leonhard Euler proved that the answer is no, and in doing so invented graph theory. The problem became the foundation for what we now call Euler paths and Euler circuits.
The Königsberg Bridge Problem
Königsberg (modern-day Kaliningrad) had four landmasses connected by seven bridges. Euler modeled each landmass as a vertex and each bridge as an edge:
Every vertex (landmass) has an odd degree: A has 3 edges, B has 5, C has 3, and D has 3. Euler proved that you need at most 2 odd-degree vertices for a traversal to exist. With four odd-degree vertices, it is impossible.
Definitions
Euler Path
A path in a graph that visits every edge exactly once. It may start and end at different vertices.
Euler Circuit
An Euler path that starts and ends at the same vertex. It forms a closed loop through every edge.
Example: A Graph with an Euler Circuit
Conditions for Existence
Before searching for an Euler path or circuit, check whether one exists. The rules differ for undirected and directed graphs.
Undirected Graphs
Euler Circuit (Undirected)
An Euler circuit exists if and only if:
- Every vertex has even degree, AND
- All vertices with non-zero degree are connected (the graph is connected considering only non-isolated vertices).
Euler Path (Undirected)
An Euler path (non-circuit) exists if and only if:
- Exactly 2 vertices have odd degree (these become the start and end), AND
- All vertices with non-zero degree are connected.
Directed Graphs
Euler Circuit (Directed)
An Euler circuit exists in a directed graph if and only if:
- Every vertex has in-degree == out-degree, AND
- All vertices with non-zero degree belong to a single strongly connected component.
Euler Path (Directed)
An Euler path exists in a directed graph if and only if:
- At most one vertex has
out-degree − in-degree == 1(the start), - At most one vertex has
in-degree − out-degree == 1(the end), - All other vertices have
in-degree == out-degree, AND - The underlying graph is weakly connected (connected ignoring edge directions).
Comparison Table
| Property | Undirected — Circuit | Undirected — Path | Directed — Circuit | Directed — Path |
|---|---|---|---|---|
| Degree condition | All even | Exactly 2 odd | in-deg == out-deg ∀v | ≤1 start, ≤1 end, rest equal |
| Connectivity | Connected | Connected | Strongly connected | Weakly connected |
| Start vertex | Any vertex | An odd-degree vertex | Any vertex | Vertex with out > in |
| Algorithm | Hierholzer's Algorithm — O(V + E) | |||
Visual: Which Graphs Have Euler Paths?
✓ Euler Circuit
All degrees even (2). Circuit: A→B→C→A
✓ Euler Path (not circuit)
Two odd-degree vertices (A, C). Path: A→B→C
✗ No Euler Path
Four odd-degree vertices → impossible.
Degree Analysis: Quick Check
Given any graph, you can determine whether an Euler path or circuit exists by counting vertex degrees. Here is the systematic procedure:
Compute the degree of every vertex
For undirected graphs, each edge contributes 1 to each endpoint. For directed graphs, track in-degree and out-degree separately.
Count odd-degree vertices (undirected) or imbalanced vertices (directed)
Undirected: count how many vertices have odd degree. Directed: count vertices where in-degree ≠ out-degree.
Decide
Undirected: 0 odd → circuit, 2 odd → path, else → neither.
Directed: All balanced → circuit. Exactly one vertex with out − in = 1 and one with in − out = 1 → path. Otherwise → neither.
Verify connectivity
Run BFS/DFS from any vertex with non-zero degree. If all non-isolated vertices are reachable, the graph is connected (for undirected) or weakly connected (for directed).
Degree Check: Visual Example
C++ Existence Check
// Check Euler path/circuit existence in an undirected graph
// Returns: 0 = neither, 1 = Euler path, 2 = Euler circuit
int checkEuler(int n, vector<vector<int>>& adj) {
// 1. Check connectivity (BFS from first non-isolated vertex)
int start = -1;
vector<int> deg(n, 0);
for (int u = 0; u < n; u++) {
deg[u] = adj[u].size();
if (deg[u] > 0 && start == -1) start = u;
}
if (start == -1) return 2; // no edges, trivially Eulerian
vector<bool> vis(n, false);
queue<int> q;
q.push(start);
vis[start] = true;
while (!q.empty()) {
int u = q.front(); q.pop();
for (int v : adj[u]) {
if (!vis[v]) { vis[v] = true; q.push(v); }
}
}
for (int u = 0; u < n; u++)
if (deg[u] > 0 && !vis[u]) return 0; // not connected
// 2. Count odd-degree vertices
int oddCount = 0;
for (int u = 0; u < n; u++)
if (deg[u] % 2 != 0) oddCount++;
if (oddCount == 0) return 2; // Euler circuit
if (oddCount == 2) return 1; // Euler path
return 0; // neither
}
Hierholzer's Algorithm
Hierholzer's algorithm (1873) is the standard O(V + E) method for finding an Euler path or circuit. The key insight: build sub-circuits and splice them together.
Why the Naive Approach Fails
A naive strategy — "just follow unused edges until you get stuck" — can fail badly. You might get stuck at a vertex that still has unused edges elsewhere in the graph. The visited edges form a sub-circuit, but many edges remain unvisited.
The Post-Order Trick
Instead of recording vertices as you enter them, record them as you leave them (when all their edges are exhausted). Then reverse the result. This guarantees that any sub-circuits are properly spliced into the main path.
Pseudocode
// stack for DFS, result built in reverse stack ← [start] circuit ← [] while stack is not empty: v ← stack.top() if v has unused edges: u ← next unused neighbor of v remove edge (v, u) stack.push(u) else: stack.pop() circuit.append(v) // post-order: add when exhausted return circuit.reverse()
How It Works
- Push start onto the stack.
- Peek at the top of the stack. If the current vertex has unused edges, pick one, mark it used, and push the neighbor.
- If the current vertex has no unused edges, pop it and add it to the circuit list (post-order).
- Repeat until the stack is empty.
- Reverse the circuit list to get the correct order.
Step-by-Step Visual Walkthrough
Let us trace Hierholzer's algorithm on our example graph with 6 nodes and 8 edges. All vertices have even degree, so an Euler circuit exists.
Graph edges: 0-1, 0-2, 1-2, 1-3, 2-3, 3-4, 3-5, 4-5
We will process neighbors in ascending order. The algorithm uses a stack (DFS) and a circuit list (post-order result).
Initialize
Push vertex 0 onto the stack.
Stack: [0] Circuit: []
From 0: traverse edge 0-1
Vertex 0 has unused edges {1, 2}. Pick neighbor 1. Mark edge 0-1 as used. Push 1.
Stack: [0, 1] Circuit: [] Used: {0-1}
From 1: traverse edge 1-2
Vertex 1 has unused edges {2, 3} (0 already used). Pick neighbor 2. Mark 1-2 used. Push 2.
Stack: [0, 1, 2] Circuit: [] Used: {0-1, 1-2}
From 2: traverse edge 2-0
Vertex 2 has unused edges {0, 3} (1 already used). Pick neighbor 0. Mark 2-0 used. Push 0.
Stack: [0, 1, 2, 0] Circuit: [] Used: {0-1, 1-2, 0-2}
Vertex 0: no unused edges → pop to circuit
Vertex 0 has no more unused edges (both 0-1 and 0-2 used). Pop 0 and add to circuit.
Stack: [0, 1, 2] Circuit: [0]
From 2: traverse edge 2-3
Back to vertex 2 (top of stack). It still has unused edge {3}. Pick 3. Mark 2-3 used. Push 3.
Stack: [0, 1, 2, 3] Circuit: [0] Used: {0-1, 1-2, 0-2, 2-3}
From 3: traverse edge 3-4
Vertex 3 has unused edges {1, 4, 5} (2 already used). Pick 4. Mark 3-4 used. Push 4.
Stack: [0, 1, 2, 3, 4] Circuit: [0] Used: + {3-4}
From 4: traverse edge 4-5
Vertex 4 has unused edge {5} (3 already used). Pick 5. Mark 4-5 used. Push 5.
Stack: [0, 1, 2, 3, 4, 5] Circuit: [0] Used: + {4-5}
From 5: traverse edge 5-3
Vertex 5 has unused edge {3} (4 already used). Pick 3. Mark 5-3 used. Push 3.
Stack: [0, 1, 2, 3, 4, 5, 3] Circuit: [0] Used: + {3-5}
From 3: traverse edge 3-1
Vertex 3 has one remaining unused edge {1}. Pick 1. Mark 3-1 used. Push 1.
Stack: [0, 1, 2, 3, 4, 5, 3, 1] Circuit: [0] Used: + {1-3} (all 8 edges now used!)
Unwind: all remaining vertices have no edges
Pop each vertex from the stack and append to circuit: 1, 3, 5, 4, 3, 2, 1, 0.
Stack: [] Circuit: [0, 1, 3, 5, 4, 3, 2, 1, 0]
Reverse to get the Euler Circuit
Reverse the circuit list:
0 → 1 → 2 → 3 → 4 → 5 → 3 → 1 → 0
This visits all 8 edges exactly once and returns to the start. ✓
Interactive Animation
Watch Hierholzer's algorithm run on the example graph. The animation colors edges as they are traversed and tracks the stack and circuit as data structure panels.
Hierholzer's Algorithm — Euler Circuit
Euler Path in Directed Graphs
For directed graphs, Hierholzer's algorithm works the same way — but edge removal is simpler (each directed edge exists only once) and the existence conditions involve in-degree and out-degree.
Directed Euler Path Example
Finding the Start Vertex (Directed)
// Find start vertex for directed Euler path
int findStart(int n, vector<vector<int>>& adj) {
vector<int> in_deg(n, 0), out_deg(n, 0);
for (int u = 0; u < n; u++) {
out_deg[u] = adj[u].size();
for (int v : adj[u]) in_deg[v]++;
}
int start = 0;
for (int u = 0; u < n; u++) {
if (out_deg[u] - in_deg[u] == 1) return u; // must start here
}
// All balanced → circuit, start anywhere with edges
for (int u = 0; u < n; u++)
if (out_deg[u] > 0) return u;
return 0;
}
C++ Implementation (Undirected)
Undirected Euler paths require careful edge tracking: when you use edge (u, v), you must also mark the reverse edge (v, u) as used. We use edge indices to handle this.
#include <bits/stdc++.h>
using namespace std;
struct Edge {
int to, id;
};
vector<int> eulerPathUndirected(int n, vector<pair<int,int>>& edges) {
vector<vector<Edge>> adj(n);
int m = edges.size();
// Build adjacency list with edge IDs
for (int i = 0; i < m; i++) {
auto [u, v] = edges[i];
adj[u].push_back({v, i});
adj[v].push_back({u, i});
}
// Find start vertex
int start = 0;
vector<int> deg(n, 0);
for (int u = 0; u < n; u++) deg[u] = adj[u].size();
for (int u = 0; u < n; u++) {
if (deg[u] % 2 == 1) { start = u; break; }
}
// Hierholzer's algorithm
vector<bool> usedEdge(m, false);
vector<int> idx(n, 0); // next edge to try for each vertex
stack<int> stk;
vector<int> circuit;
stk.push(start);
while (!stk.empty()) {
int u = stk.top();
if (idx[u] < (int)adj[u].size()) {
auto& e = adj[u][idx[u]];
idx[u]++;
if (usedEdge[e.id]) continue; // skip used edges
usedEdge[e.id] = true;
stk.push(e.to);
} else {
circuit.push_back(u);
stk.pop();
}
}
reverse(circuit.begin(), circuit.end());
return circuit; // size = m + 1 for circuit, m + 1 for path
}
int main() {
int n = 6;
vector<pair<int,int>> edges = {
{0,1}, {0,2}, {1,2}, {1,3}, {2,3}, {3,4}, {3,5}, {4,5}
};
auto path = eulerPathUndirected(n, edges);
for (int v : path) cout << v << " ";
// Output: 0 1 2 3 4 5 3 1 0 (or similar valid Euler circuit)
return 0;
}
idx[u] pointer avoids rescanning used edges, keeping the total work O(V + E).
Complexity Analysis
| Aspect | Complexity | Explanation |
|---|---|---|
| Time | O(V + E) | Every edge is visited exactly once (pushed onto stack, then marked used). The idx[] pointer ensures we never rescan used edges. |
| Space | O(V + E) | Adjacency list: O(V + E). Stack: O(E) worst case. Circuit result: O(E). Used-edge array: O(E). |
| Existence check | O(V + E) | BFS/DFS for connectivity + degree counting. |
Directed Graph Implementation
Directed graphs are simpler to implement: each edge exists in only one direction, so there is no need for edge IDs or dual marking.
#include <bits/stdc++.h>
using namespace std;
vector<int> eulerPathDirected(int n, vector<pair<int,int>>& edges) {
vector<vector<int>> adj(n);
vector<int> in_deg(n, 0), out_deg(n, 0);
for (auto [u, v] : edges) {
adj[u].push_back(v);
out_deg[u]++;
in_deg[v]++;
}
// Sort neighbors for deterministic output (optional)
for (int u = 0; u < n; u++)
sort(adj[u].begin(), adj[u].end());
// Find start vertex
int start = 0;
for (int u = 0; u < n; u++) {
if (out_deg[u] - in_deg[u] == 1) { start = u; break; }
if (out_deg[u] > 0) start = u;
}
// Hierholzer's with index pointers
vector<int> idx(n, 0);
stack<int> stk;
vector<int> circuit;
stk.push(start);
while (!stk.empty()) {
int u = stk.top();
if (idx[u] < (int)adj[u].size()) {
stk.push(adj[u][idx[u]++]);
} else {
circuit.push_back(u);
stk.pop();
}
}
reverse(circuit.begin(), circuit.end());
return circuit;
}
int main() {
int n = 4;
// Directed edges forming an Euler circuit
vector<pair<int,int>> edges = {
{0,1}, {1,2}, {2,3}, {3,0}, {0,2}, {2,0}
};
auto path = eulerPathDirected(n, edges);
for (int v : path) cout << v << " ";
// Output: 0 1 2 0 2 3 0 (or similar valid Euler circuit)
return 0;
}
Undirected Implementation
- Need unique edge IDs
usedEdge[id]marks both directionsidx[u]pointer + skip used edges- Slightly more complex bookkeeping
Directed Implementation
- No edge IDs needed
- Just advance
idx[u]pointer - Each edge consumed once automatically
- Simpler and cleaner code
Applications
Euler paths and circuits appear in many practical and theoretical contexts:
Circuit Board Testing (PCB Inspection)
A testing probe must trace every connection on a printed circuit board. If the connections form a graph with an Euler path, the probe can test every wire in a single pass without backtracking — minimizing testing time and wear.
DNA Fragment Assembly (de Bruijn Graphs)
In genome sequencing, DNA is shattered into short k-mers. Each k-mer becomes an edge in a de Bruijn graph (connecting its (k−1)-prefix to its (k−1)-suffix). Finding an Euler path in this directed graph reconstructs the original DNA sequence. This is the foundation of modern genome assemblers like Velvet and SPAdes.
Chinese Postman Problem
A mail carrier must traverse every street (edge) at least once and return to the post office. If the graph has an Euler circuit, the optimal route is exactly that circuit. Otherwise, we find the minimum set of edges to duplicate so that all degrees become even, then find the Euler circuit on the augmented graph.
Network Routing & de Bruijn Sequences
A de Bruijn sequence of order n is a cyclic string over an alphabet where every possible substring of length n appears exactly once. Finding it is equivalent to finding an Euler circuit in a de Bruijn graph. This has applications in combinatorial testing, pseudo-random number generation, and lock cracking (e.g., LeetCode 753: Cracking the Safe).
Interview & CP Problems
Reconstruct Itinerary Hard
LeetCode 332. Given a list of airline tickets [from, to], reconstruct the itinerary in lexicographic order starting from "JFK". This is a directed Euler path problem where you sort neighbors alphabetically and apply Hierholzer's algorithm.
Key insight: Sort adjacency lists, use Hierholzer's post-order approach, reverse at the end. The post-order ensures we don't get stuck on a dead-end branch.
Valid Arrangement of Pairs Hard
LeetCode 2097. Given pairs [start, end], arrange them so the end of each pair equals the start of the next. Each pair is a directed edge. Find the Euler path.
Key insight: Build directed graph, find start vertex (out-degree − in-degree == 1), run Hierholzer's. The result gives the order of pairs.
Cracking the Safe Hard
LeetCode 753. Find the shortest string containing all possible passwords of length n using k digits. This is a de Bruijn sequence problem — find an Euler circuit in a de Bruijn graph of order n−1.
Key insight: Nodes are (n−1)-digit strings, edges are n-digit strings. An Euler circuit gives the optimal sequence.
Minimum Number of Days to Disconnect Island Medium
Codeforces / CP classic. Find Euler path in a grid graph. While not a direct Euler problem, understanding edge traversal and connectivity analysis is essential for these graph decomposition problems.
Summary
Decision Tree: Circuit vs Path vs Neither
- Check connectivity — if the graph is disconnected (ignoring isolated vertices), no Euler path/circuit exists.
- Count odd-degree vertices (undirected) or degree imbalances (directed).
- 0 odd / all balanced → Euler Circuit. Start at any vertex.
- 2 odd / one start one end → Euler Path. Start at an odd-degree vertex (or the vertex with out > in).
- More than 2 odd / other imbalances → No Euler path exists.
- Apply Hierholzer's Algorithm in O(V + E) time.
Key Takeaways
- Euler path visits every edge exactly once; Hamiltonian path visits every vertex exactly once.
- Existence can be checked in O(V + E) by counting degrees and verifying connectivity.
- Hierholzer's algorithm finds the path/circuit in O(V + E) using the post-order trick.
- For undirected graphs, track edges by ID to avoid double-using an edge.
- For directed graphs, simply advance the adjacency pointer — each edge is consumed once.
- Real-world applications include DNA sequencing (de Bruijn graphs), circuit testing, and the Chinese Postman Problem.
Quick Reference
| Operation | Time | Space |
|---|---|---|
| Existence check (degree + BFS) | O(V + E) | O(V) |
| Find Euler path/circuit (Hierholzer) | O(V + E) | O(V + E) |
| Find start vertex | O(V) | O(1) |