← All Posts
DSA Series · Graphs · Euler Path

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:

A (3) B (5) C (3) D (3)
The Königsberg Bridge Problem — each number shows the vertex degree. All degrees are odd, so no Euler path exists.

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.

Key distinction: An Euler path/circuit visits every edge exactly once. A Hamiltonian path/circuit visits every vertex exactly once. The Hamiltonian problem is NP-complete; the Euler problem is solvable in linear time.

Example: A Graph with an Euler Circuit

0 1 2 3 4 5 deg 2 deg 4 deg 4 deg 4 deg 4 deg 2
All vertices have even degree (0&5: deg 2, 1–4: deg 4) → an Euler circuit exists. One valid circuit: 0→1→3→4→5→3→2→4→1→2→0 — but finding it systematically requires Hierholzer's algorithm.

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.
Quick count: Count vertices with odd degree. If 0 → Euler circuit. If 2 → Euler path (start at one odd vertex). Otherwise → no Euler path exists.

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

A 2 B 2 C 2

All degrees even (2). Circuit: A→B→C→A

✓ Euler Path (not circuit)

A 1 B 2 C 1

Two odd-degree vertices (A, C). Path: A→B→C

✗ No Euler Path

M 4 A 1 B 1 C 1 D 1

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

0 1 2 3 4 5 deg 2 ✓ deg 4 ✓ deg 4 ✓ deg 4 ✓ deg 2 ✓ deg 2 ✓
All vertices have even degree → Euler circuit exists. Graph from our running 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.

Getting stuck: If you greedily walk from vertex 0 and happen to return to 0 before visiting all edges, you are stuck. The remaining unvisited edges might form separate components that you can no longer reach. Hierholzer's algorithm handles this by using a post-order approach.

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

function findEulerPath(graph, start):
// 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

  1. Push start onto the stack.
  2. Peek at the top of the stack. If the current vertex has unused edges, pick one, mark it used, and push the neighbor.
  3. If the current vertex has no unused edges, pop it and add it to the circuit list (post-order).
  4. Repeat until the stack is empty.
  5. Reverse the circuit list to get the correct order.
Why post-order? When we exhaust all edges from a vertex, we know it forms a complete sub-circuit. By adding it to the result last, any side-trips (sub-circuits branching off) are naturally inserted in the correct position when we reverse at the end.

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

0 1 2 3 4 5 e0 e1 e2 e3 e4 e5 e6 e7
Adjacency: 0→{1,2}, 1→{0,2,3}, 2→{0,1,3}, 3→{1,2,4,5}, 4→{3,5}, 5→{3,4}. Start at vertex 0.

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

Press Play or Step to begin.

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

0 1 2 3 4 5 out:2 in:1 out:2 in:2 out:1 in:1 out:1 in:1 out:1 in:2 out:0 in:1
Directed graph: vertex 0 has out−in = 1 (start), vertex 5 has in−out = 1 (end). All others balanced. Euler path: 0→1→2→5←… Actually: 0→3→4→1→2→5 visits all 7 edges? Let's count: edges are 0→1, 0→3, 3→4, 1→4, 1→2, 2→5, 4→5. Path: 0→1→2→5 misses edges. Correct Euler path: 0→1→4→5←… We need the algorithm!

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;
}
Edge tracking trick: By assigning each undirected edge a unique ID and storing it in both directions, we mark an edge as used once and it is automatically unavailable from both endpoints. The 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.
Optimal: Since we must look at every edge at least once, O(V + E) is the best possible time complexity. Hierholzer's is optimal.

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 directions
  • idx[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

  1. Check connectivity — if the graph is disconnected (ignoring isolated vertices), no Euler path/circuit exists.
  2. Count odd-degree vertices (undirected) or degree imbalances (directed).
  3. 0 odd / all balancedEuler Circuit. Start at any vertex.
  4. 2 odd / one start one endEuler Path. Start at an odd-degree vertex (or the vertex with out > in).
  5. More than 2 odd / other imbalancesNo Euler path exists.
  6. Apply Hierholzer's Algorithm in O(V + E) time.

Key Takeaways

Quick Reference

OperationTimeSpace
Existence check (degree + BFS)O(V + E)O(V)
Find Euler path/circuit (Hierholzer)O(V + E)O(V + E)
Find start vertexO(V)O(1)