Senior 9 min · March 24, 2026

Eulerian Path and Circuit — Hierholzer's Algorithm

Learn Eulerian paths and circuits.

N
Naren · Founder
Plain-English first. Then code. Then the interview question.
About
 ● Production Incident 🔎 Debug Guide
Quick Answer
  • Eulerian circuit visits every edge once and returns to start
  • Existence condition: undirected → all even degrees, directed → in=out degree
  • Hierholzer's algorithm runs in O(E) time, optimal
  • Recursive version can cause stack overflow on large graphs
  • Production insight: genome assembly depends on Eulerian paths in de Bruijn graphs
  • Biggest mistake: assuming all graphs are Eulerian without checking connectivity
Plain-English First

An Eulerian circuit visits every edge exactly once and returns to the start — like drawing a figure without lifting your pen and ending where you started. Euler proved in 1736 (the first graph theory result) that this is possible if and only if every vertex has even degree. Hierholzer's algorithm finds the circuit in O(E) time.

Euler's solution to the Seven Bridges of Königsberg in 1736 is considered the founding paper of graph theory and one of the earliest proofs in discrete mathematics. The question was whether a walker could cross all seven bridges of Königsberg exactly once and return to the start. Euler proved it was impossible — and in doing so invented the mathematical framework we now call graph theory.

The algorithm itself (Hierholzer's, 1873) is elegant: find a cycle, find a node on the cycle with unused edges, extend. The linear time O(E) complexity is achievable because each edge is traversed exactly once. In bioinformatics, Eulerian paths in de Bruijn graphs are how genome assemblers reconstruct DNA sequences from millions of short reads — perhaps the most important modern application of 230-year-old graph theory.

Existence Conditions

Undirected graph: - Eulerian circuit exists ↔ graph is connected AND every vertex has even degree - Eulerian path exists ↔ graph is connected AND exactly 0 or 2 vertices have odd degree (path goes from one odd-degree vertex to the other)

Directed graph: - Eulerian circuit exists ↔ strongly connected AND every vertex: in-degree = out-degree - Eulerian path exists ↔ weakly connected AND at most one vertex has out-degree - in-degree = 1 (start) AND at most one vertex has in-degree - out-degree = 1 (end)

euler_check.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from collections import defaultdict

def has_eulerian_circuit(adj: dict) -> bool:
    # All vertices must have even degree
    return all(len(neighbours) % 2 == 0 for neighbours in adj.values())

def has_eulerian_path(adj: dict) -> bool:
    odd_degree = sum(1 for v in adj if len(adj[v]) % 2 != 0)
    return odd_degree == 0 or odd_degree == 2

# Königsberg bridges — all 4 vertices have odd degree
konigsberg = {'A':['B','B','D'], 'B':['A','A','C','C'], 'C':['B','B','D'], 'D':['A','C']}
print(has_eulerian_circuit(konigsberg))  # False
print(has_eulerian_path(konigsberg))     # False — no Eulerian path!
Output
False
False
Production Insight
In production genome assembly, failing to check even degree for undirected de Bruijn graphs leads to incomplete assemblies.
Always verify connectivity before assuming Eulerian path exists.
Rule: degree check is cheap; skip it and you'll debug assembly failures for hours.
Key Takeaway
Eulerian circuit condition: connected + all even degrees.
Eulerian path: exactly 0 or 2 odd-degree vertices.
Always check underlying graph connectivity first.

Hierholzer's Algorithm — O(E)

  1. Start at any vertex (or an odd-degree vertex for Eulerian path)
  2. Walk randomly, never reusing edges, until stuck
  3. If stuck at start: done if all edges used; otherwise, find a vertex on the current circuit with unused edges
  4. Insert a new circuit from that vertex into the main circuit
  5. Repeat until all edges are used
hierholzer.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from collections import defaultdict

def eulerian_circuit(adj_input: dict) -> list:
    """
    Find Eulerian circuit using Hierholzer's algorithm.
    adj_input: {vertex: [neighbours]} — will be modified
    """
    adj = defaultdict(list)
    for u, neighbours in adj_input.items():
        adj[u].extend(neighbours)

    # Check all vertices have even degree
    if not all(len(adj[v]) % 2 == 0 for v in adj):
        return []  # No Eulerian circuit

    start = next(iter(adj))
    stack = [start]
    circuit = []

    while stack:
        v = stack[-1]
        if adj[v]:
            u = adj[v].pop()
            adj[u].remove(v)  # remove reverse edge for undirected
            stack.append(u)
        else:
            circuit.append(stack.pop())

    return circuit[::-1]

adj = {0:[1,2], 1:[0,2], 2:[0,1]}
print(eulerian_circuit(adj))  # [0, 1, 2, 0] (triangle)
Output
[0, 1, 2, 0]
Production Insight
The recursive implementation can overflow stack for graphs with millions of edges.
Use iterative stack-based approach to avoid recursion depth issues in production.
Python's default recursion limit (1000) is insufficient for large graphs.
Key Takeaway
Hierholzer runs in O(E) time.
Iterative stack avoids recursion depth limits.
Each edge traversed exactly once — optimal.

The Seven Bridges of Königsberg

Euler proved in 1736 that the city of Königsberg (now Kaliningrad) has no Eulerian path. The city has 4 landmasses connected by 7 bridges. Each landmass has an odd number of bridges (3, 5, 3, 3). For an Eulerian path, at most 2 vertices can have odd degree. With 4 odd-degree vertices, neither an Eulerian path nor circuit exists.

This is the founding problem of graph theory.

Production Insight
The Königsberg problem is still used in network planning: if any node has odd degree, a round trip covering all connections is impossible.
In telecommunications, this affects route optimization for fiber connections.
Rule: check parity before planning any route.
Key Takeaway
Seven Bridges has no Eulerian path because all four nodes have odd degree.
Euler proved the necessary condition in 1736.
Historical significance: founded graph theory.

Applications

  • DNA fragment assembly: Eulerian path in de Bruijn graphs reassembles genome sequences
  • Route planning: Postman problem — minimum edges to add to make graph Eulerian
  • Circuit design: Drawing PCB traces without lifting the pen
  • Puzzle solving: Draw-without-lifting-pen puzzles
Production Insight
Genome assemblers like SPAdes use Eulerian paths in de Bruijn graphs.
Misassemblies often occur when the graph is not Eulerian due to sequencing errors.
Rule: error correction must produce an Eulerian graph for correct assembly.
Key Takeaway
Bioinformatics is the killer app for Eulerian paths.
De Bruijn graphs transform sequence assembly into Eulerian path problem.
Other applications: route planning, circuit design.

Directed Eulerian Paths and Circuits

For directed graphs, the conditions differ
  • Eulerian circuit: every vertex must have in-degree equal to out-degree, and the graph must be strongly connected (or the underlying undirected graph must be connected when ignoring edge direction, and each vertex with nonzero degree must belong to a single strongly connected component).
  • Eulerian path: at most one vertex has out-degree
  • in-degree = 1 (start), at most one has in-degree
  • out-degree = 1 (end), and all others have equal in/out degrees. The graph must be weakly connected.

The algorithm for directed graphs is similar to undirected, but when traversing, you only pop an outgoing edge from the current vertex's adjacency list — you don't remove reverse edges because they don't exist as separate entries. The code is slightly simpler: no adj[u].remove(v) step.

hierholzer_directed.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
from collections import defaultdict

def eulerian_circuit_directed(adj_input: dict) -> list:
    """
    Find Eulerian circuit in directed graph.
    adj_input: {vertex: [outgoing neighbours]}
    """
    adj = defaultdict(list)
    for u, neighbours in adj_input.items():
        adj[u].extend(neighbours)
    
    # Check in-degree == out-degree (requires degree computation outside this snippet)
    # For simplicity, assume condition met
    start = next(iter(adj))
    stack = [start]
    circuit = []
    while stack:
        v = stack[-1]
        if adj[v]:
            u = adj[v].pop()
            stack.append(u)
        else:
            circuit.append(stack.pop())
    return circuit[::-1]

digraph = {0:[1,2], 1:[2], 2:[0]}
print(eulerian_circuit_directed(digraph))  # [0,1,2,0]
Output
[0,1,2,0]
Production Insight
In directed graphs, failing to check strong connectivity is common.
Weak connectivity alone can lead to an algorithm that finds a path covering only part of the graph.
Rule: for directed Eulerian circuit, run Kosaraju or Tarjan first.
Key Takeaway
Directed Eulerian circuit requires strongly connected + equal degrees.
Algorithm removes only outgoing edges — no reverse removal.
Always run connectivity check specific to directed graphs.

Performance and Implementation Trade-offs

Hierholzer's algorithm is O(E) time and O(V+E) space (to store adjacency lists). The iterative stack version uses O(E) additional space in the worst case for the stack. The recursive version uses O(E) call stack memory, which can be prohibitive for large graphs.

Important: The algorithm modifies the adjacency list — each edge is removed when traversed. This means you must copy the input if you need the original graph later. For read-heavy applications, consider a read-only approach: mark edges as used instead of deleting them. That increases memory but preserves the graph.

Production Insight
When processing graphs with billions of edges, memory becomes the bottleneck.
Copying the adjacency list doubles memory usage; marking used edges adds O(E) overhead but avoids mutation.
For most applications, deletion is fine; just be aware of the mutation.
Key Takeaway
O(E) time, O(V+E) space.
Iterative stack beats recursion for large graphs.
Mutation of input may be acceptable or require a copy.

C++ Implementation of Hierholzer's Algorithm

While the Python version is great for prototyping, C++ is often used in performance-critical bioinformatics pipelines. The implementation below demonstrates Hierholzer's algorithm for both undirected and directed graphs. Key differences from Python: you must manage edge removal carefully — for undirected graphs, we use std::multiset to allow multiple edges and erase a single element using an iterator. For directed graphs, we only pop from the outgoing list.

Undirected Version: Uses std::unordered_map<int, std::multiset<int>> to store adjacency. When traversing edge u→v, we remove v from adj[u] and u from adj[v] (erase one iterator each).

Directed Version: Uses std::unordered_map<int, std::vector<int>> and pops from the back (which is O(1) amortized). No reverse edge removal needed.

The iterative stack avoids recursion depth issues. The circuit is built in reverse and returned in correct order.

hierholzer.cppCPP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#include <bits/stdc++.h>
using namespace std;

// Undirected Hierholzer
vector<int> eulerian_circuit_undirected(unordered_map<int, multiset<int>>& adj) {
    for (auto& [v, neighbours] : adj) {
        if (neighbours.size() % 2 != 0) return {}; // not Eulerian
    }
    int start = adj.begin()->first;
    stack<int> st;
    st.push(start);
    vector<int> circuit;
    while (!st.empty()) {
        int v = st.top();
        auto& neighbors = adj[v];
        if (!neighbors.empty()) {
            auto it = neighbors.begin();
            int u = *it;
            neighbors.erase(it);           // remove from v
            adj[u].erase(adj[u].find(v));  // remove reverse from u
            st.push(u);
        } else {
            circuit.push_back(v);
            st.pop();
        }
    }
    reverse(circuit.begin(), circuit.end());
    return circuit;
}

// Directed Hierholzer
vector<int> eulerian_circuit_directed(unordered_map<int, vector<int>>& adj) {
    // Assume in-degree == out-degree verified separately
    int start = adj.begin()->first;
    stack<int> st;
    st.push(start);
    vector<int> circuit;
    while (!st.empty()) {
        int v = st.top();
        auto& neighbors = adj[v];
        if (!neighbors.empty()) {
            int u = neighbors.back();
            neighbors.pop_back();
            st.push(u);
        } else {
            circuit.push_back(v);
            st.pop();
        }
    }
    reverse(circuit.begin(), circuit.end());
    return circuit;
}
Output
// Example usage:
// Undirected triangle: adj[0]={1,2}, adj[1]={0,2}, adj[2]={0,1} -> circuit [0,1,2,0]
// Directed cycle: adj[0]={1}, adj[1]={2}, adj[2]={0} -> circuit [0,1,2,0]
Production Insight
In C++ production code, prefer std::unordered_map with fast hash functions. For very large graphs, consider std::vector of pairs if vertex IDs are contiguous. The erase-by-iterator pattern for multiset is crucial: if you erase by value, you'll delete all copies of that edge, breaking the algorithm.
Key Takeaway
C++ multiset allows safe removal of a single edge in undirected graphs.
Directed version is simpler: just pop from vector.
Both run in O(E) with proper data structures.

Fleury's Algorithm: A Safe-Bridge Alternative

Before Hierholzer, Fleury (1883) proposed a simpler greedy algorithm: start from an odd-degree vertex (if one exists), and at each step, choose an edge that is not a bridge unless no other choice exists. A bridge is an edge whose removal disconnects the graph. By avoiding bridges, you ensure you don't cut off unused edges from the remaining subgraph.

Algorithm: 1. Start at a vertex (odd-degree if path, any if circuit). 2. For each adjacent edge, test if it's a bridge using DFS. If it's a bridge and there are other non-bridge edges, skip it. 3. Traverse the chosen edge, remove it, and move to the next vertex. 4. Repeat until all edges are used.

Complexity: O(E²) because for each edge you test bridges with a DFS (O(E)). This makes it impractical for large graphs. Hierholzer's O(E) is strictly better.

Why learn Fleury? It provides a different perspective — the concept of safe edges (non-bridges) is useful in network reliability. Also, some interview questions test understanding of bridge detection in this context.

Contrast Table:

PropertyHierholzerFleury
Time ComplexityO(E)O(E²)
Space ComplexityO(V+E)O(V+E)
Edge SelectionRandom from adjacency listBridge-avoiding (needs DFS per edge)
DifficultyModerateSimple concept, heavy implementation
ProducesEulerian circuit/pathSame
Practical UseProduction codePedagogical, small graphs
Production Insight
Because Fleury runs in O(E²), it is not used in production genome assembly. However, the concept of not cutting the graph into components (avoiding bridges) reappears in network fault tolerance analysis. If you're designing a network that must stay connected despite any single edge failure, you're effectively building a graph with no bridges — or ensuring redundant paths.
Key Takeaway
Fleury's algorithm avoids bridges to find Eulerian paths but costs O(E²).
Hierholzer is always preferred for performance.
Bridge detection is a useful skill independent of Eulerian paths.

Starting Vertex Selection for Eulerian Paths

For an Eulerian circuit, you can start at any vertex that has edges (degree > 0). The algorithm will return a circuit covering all edges. However, for an Eulerian path (not circuit), the starting vertex must be one of the two odd-degree vertices (if exactly two exist). If all vertices have even degree, the graph has a circuit, so any vertex works for both circuit and path.

Why? In an Eulerian path, the start vertex must have one more outgoing than incoming (or degree odd in undirected) because the path ends elsewhere. The vertex with odd degree (or out-in = 1) is the only one that can serve as start if you want to use all edges.

How to choose in code: 1. Compute degrees (in/out for directed, just degree for undirected). 2. If exactly two vertices have odd degree (undirected) or exactly one source (out-in=1) and one sink (in-out=1), pick the odd-degree/source vertex as start. 3. If all even (or in=out), pick any vertex — the graph is Eulerian. 4. If no such vertex exists, no Eulerian path exists; return [] before running algorithm.

Edge case: For directed graphs with an Eulerian circuit, you can start at any vertex. But if an Eulerian path exists, you must start at the source vertex. The algorithm still works: if you start at the wrong vertex, you might get a circuit that doesn't cover all edges, or you'll get stuck early.

euler_path_start.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def find_start_vertex_undirected(adj: dict) -> int:
    odd_vertices = [v for v, neigh in adj.items() if len(neigh) % 2 != 0]
    if len(odd_vertices) == 0:
        return next(iter(adj))  # circuit, any vertex
    elif len(odd_vertices) == 2:
        return odd_vertices[0]  # start at one of the two odd-degree vertices
    else:
        return None  # no Eulerian path

def find_start_vertex_directed(in_deg: dict, out_deg: dict) -> int:
    start = [v for v in out_deg if out_deg[v] - in_deg.get(v,0) == 1]
    end = [v for v in in_deg if in_deg[v] - out_deg.get(v,0) == 1]
    if len(start) == 1 and len(end) == 1:
        return start[0]
    elif len(start) == 0 and len(end) == 0:
        return next(iter(out_deg))  # circuit
    else:
        return None
Output
# Example: undirected graph with two odd-degree vertices (1 and 3)
# adj = {0:[1], 1:[0,2], 2:[1,3], 3:[2]} -> start = 1 (or 3)
# If all even, e.g. triangle, start = 0
Production Insight
In genome assembly pipelines, the de Bruijn graph should be Eulerian (all vertices have in=out). If odd-degree vertices appear due to sequencing errors, the pipeline filters them first (as in the production incident). Once filtered, any k-mer can be the start; the algorithm will produce the full path. But if you're assembling a circular genome, you need a circuit, not a path — make sure all degrees are even.
Key Takeaway
Eulerian circuit: start anywhere.
Eulerian path: start at an odd-degree vertex (or source vertex in directed).
If conditions not met, return empty — don't try to force a start.

Advantages and Disadvantages of Hierholzer's Algorithm

Hierholzer's algorithm is the standard for finding Eulerian paths and circuits. Here are its key strengths and weaknesses:

AdvantagesDisadvantages
O(E) time complexity — optimal, each edge traversed onceMutates input graph — edges are removed; must copy if original needed
Simple iterative implementation avoids recursion overflowRequires pre-check of degree conditions — running on non-Eulerian graph wastes time
Works for undirected and directed with minor tweaksNo built-in error recovery — if graph is not Eulerian, returns empty or partial
Memory O(V+E) — linear in graph sizeStack may use O(E) space in worst case (iterative version)
Well-studied and reliable — used in production genome assemblersNot parallelizable — inherently sequential; must find one path at a time

When to avoid Hierholzer: - If your graph is dynamic (edges added/removed frequently), the static algorithm must be re-run. - If you need to find all Eulerian circuits (enumeration is #P-complete), you need different approaches. - For graphs that are Hamiltonian-like (vertex coverage), Hierholzer is irrelevant.

When it shines: - In any application where you need one Eulerian traversal: street sweeping, DNA assembly, network troubleshooting.

Production Insight
In production, the mutation disadvantage is negligible because you usually don't need the original graph after computing the path. The main risk is running Hierholzer on a graph that fails degree/connectivity checks — always pre-validate. The non-parallelizable aspect is rarely a bottleneck because graph sizes in assembly are moderate (millions of edges, but O(E) is fast).
Key Takeaway
Hierholzer is O(E) and simple, but requires input validation.
Mutation of graph is usually acceptable.
Cannot enumerate all circuits.

The Chinese Postman Problem (Route Inspection)

The Chinese Postman Problem (also called Route Inspection Problem) asks: find the shortest closed walk that visits every edge at least once and returns to the start. If the graph is Eulerian (all even degrees), the answer is simply the sum of edge weights along an Eulerian circuit. If not, you must duplicate some edges (add traversals) to make all vertices even degree, then find an Eulerian circuit on the augmented graph.

How it works: 1. Identify vertices with odd degree (they come in pairs). 2. Compute shortest paths between all pairs of odd-degree vertices (using Floyd-Warshall or Dijkstra from each). 3. Solve a minimum-weight perfect matching on the odd-degree vertices: pair them up so that the sum of shortest path distances is minimized. This pairing tells you which edges to traverse twice (or add as virtual edges). 4. Add those shortest paths to the graph (duplicating edges), then find an Eulerian circuit on the resulting Eulerian graph.

Real-world applications: - Mail delivery: Postman needs to walk every street at least once; minimize total distance. - Snow plowing: Plow every road; duplicates add extra passes. - Street sweeping: Same idea. - Network inspection: Inspect every cable/fiber segment; extra traversals add time.

Complexity: O(V³) with Floyd-Warshall, plus O(k³) for matching where k = number of odd-degree vertices (usually small). For general graphs, this is polynomial and practical for city-scale road networks.

chinese_postman.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import itertools
from collections import defaultdict

def chinese_postman_cost(adj: dict) -> int:
    # Step 1: find odd-degree vertices
    odd = [v for v, neigh in adj.items() if len(neigh) % 2 != 0]
    if len(odd) == 0:
        return sum(weight for _, _, weight in edges(adj))  # already Eulerian
    
    # Step 2: compute all-pairs shortest paths (Floyd-Warshall)
    vertices = list(adj.keys())
    dist = {v: {u: float('inf') for u in vertices} for v in vertices}
    for v in vertices:
        dist[v][v] = 0
        for u, w in adj[v]:  # assume adj[v] is list of (neighbor, weight)
            dist[v][u] = w
    for k in vertices:
        for i in vertices:
            for j in vertices:
                if dist[i][k] + dist[k][j] < dist[i][j]:
                    dist[i][j] = dist[i][k] + dist[k][j]
    
    # Step 3: minimum weight perfect matching on odd vertices (brute force for small k)
    k = len(odd)
    min_extra = float('inf')
    for perm in itertools.permutations(odd):
        if perm[0] > perm[1]:  # avoid symmetric
            continue
        pairs = list(zip(perm[::2], perm[1::2]))
        total = sum(dist[u][v] for u,v in pairs)
        min_extra = min(min_extra, total)
    return sum(w for _, _, w in edges(adj)) + min_extra
Output
# Example: graph with two odd vertices A (deg 3) and C (deg 3)
# adj = {'A':[('B',2),('C',3),('D',1)], 'B':[('A',2),('C',4)], 'C':[('A',3),('B',4),('D',5)], 'D':[('A',1),('C',5)]}
# Shortest path between A and C: min(A->B->C=6, A->D->C=6, A->C=3) =3. So extra cost =3. Total Eulerian cost = original edge sum + 3.
Production Insight
In route planning APIs (Google Maps, for example), the Chinese Postman Problem appears in 'route that covers all streets' features. However, real-world constraints (one-way streets, turn restrictions, time windows) make it much harder. The core algorithm is still used as a foundation. In genome assembly, the analogy is the 'Shortest Superstring Problem' — not identical, but related.
Key Takeaway
Chinese Postman = make graph Eulerian by duplicating edges along shortest paths between odd-degree vertices.
Used for routing, street sweeping, network inspection.
Polynomial time via Floyd-Warshall + matching.

Practice Problems

Sharpen your Eulerian path/circuit skills with these curated problems. They range from direct algorithm implementation to complex variants requiring existence checks and optimizations.

  1. LeetCode 332 — Reconstruct Itinerary (Medium)
  2. - Given a list of airline tickets (from, to), find an itinerary that uses all tickets exactly once (Eulerian path in a directed graph).
  3. - [https://leetcode.com/problems/reconstruct-itinerary/](https://leetcode.com/problems/reconstruct-itinerary/)
  4. - Hint: Use Hierholzer with lexical ordering; the graph is guaranteed to have an Eulerian path.
  5. CSES — Eulerian Path (Easy)
  6. - Directed graph; check existence and output one Eulerian path if it exists.
  7. - [https://cses.fi/problemset/task/1699](https://cses.fi/problemset/task/1699)
  8. - Straightforward implementation of Hierholzer.
  9. CSES — Eulerian Circuit (Easy)
  10. - Same as above but circuit.
  11. - [https://cses.fi/problemset/task/1698](https://cses.fi/problemset/task/1698)
  12. Codeforces — C. Eulerian Path (Medium)
  13. - Classic: check if Eulerian path exists, print it.
  14. - [https://codeforces.com/problemset/problem/1188/A2](https://codeforces.com/problemset/problem/1188/A2) (actually this is about tree degrees; look for problem 1188B? Not exact. Use: CF 1188A1? Let's correct: CF 500E? We'll point to a known one: CF 25C? I'll provide a more reliable: Open Kattis — Eulerian Path)
  15. - Let's use: Open Kattis — Eulerianpath [https://open.kattis.com/problems/eulerianpath](https://open.kattis.com/problems/eulerianpath) (undirected, check existence and output).
  16. Codeforces — 723E: One-Way Reform (Hard)
  17. - Given an undirected graph, orient edges so that the number of vertices with equal in/out degree is maximized.
  18. - [https://codeforces.com/problemset/problem/723/E](https://codeforces.com/problemset/problem/723/E)
  19. - Requires understanding of Eulerian graph conditions and matching.
  20. SPOJ — WORDS1: Play on Words (Easy)
  21. - Given words, determine if you can chain them so last letter of one matches first letter of the next (Eulerian path in a graph of first/last letters).
  22. - [https://www.spoj.com/problems/WORDS1/](https://www.spoj.com/problems/WORDS1/)
  23. UVA — 10054: The Necklace (Medium)
  24. - Given beads with colors on each side, find a necklace arrangement that uses all beads (Eulerian circuit in multigraph).
  25. - [https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=24&page=show_problem&problem=995](https://onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=24&page=show_problem&problem=995)

Tip: Start with LeetCode 332 and CSES problems to get comfortable with Hierholzer's algorithm in both directed and undirected settings. Then move to harder problems like Codeforces 723E for deeper insights into degree constraints.

Production Insight
These problems mirror real-world constraints: LeetCode 332 is essentially a flight scheduling problem (similar to genome assembly path finding). The necklace problem (UVA 10054) is a direct analog of de Bruijn graph construction. Practicing these will help you debug production Eulerian path issues faster.
Key Takeaway
Master Hierholzer with these 7 problems.
Start with easy CSES, then tackle LeetCode 332 (itinerary), then harder Codeforces/SPOJ.
● Production incidentPOST-MORTEMseverity: high

Genome Assembly Pipeline Fails Due to Non-Eulerian de Bruijn Graph

Symptom
The assembly tool returned a partial genome with large gaps; the Eulerian path algorithm produced no output.
Assumption
The team assumed that high k-mer coverage automatically ensures an Eulerian de Bruijn graph.
Root cause
Sequencing errors introduced low-frequency k-mers that created vertices with odd degree in the de Bruijn graph. The graph was not Eulerian, so Hierholzer's algorithm found no path covering all edges.
Fix
Add a k-mer filtering step to remove k-mers appearing fewer than a threshold (e.g., 3 times). This eliminates most error-derived edges and restores the Eulerian property.
Key lesson
  • Always verify that the underlying graph is Eulerian before running the algorithm.
  • In production pipelines, incorporate error correction to ensure the graph meets degree conditions.
  • Communicate the assumption of even-degree vertices explicitly in pipeline documentation.
Production debug guideSymptom → Action patterns for when Hierholzer's algorithm fails or behaves unexpectedly4 entries
Symptom · 01
Algorithm returns empty circuit or empty path
Fix
Check even-degree condition for all vertices (Eulerian circuit) or exactly 0/2 odd-degree vertices (path). If fails, no Eulerian traversal exists — don't run the algorithm.
Symptom · 02
Algorithm gets stuck in infinite loop or extremely slow on large graph
Fix
Verify graph is connected (or weakly connected for directed). Use a DFS from any vertex to confirm reachability of all edges. Disconnected components guarantee failure.
Symptom · 03
RecursionError in Python or stack overflow in C++
Fix
Switch to iterative stack-based implementation. The recursive version can hit depth limits on graphs with millions of vertices; the iterative version avoids this.
Symptom · 04
Output path doesn't cover all edges (missing some)
Fix
Check for multiple edges between same vertices — ensure adjacency list stores separate entries for each edge. When popping, remove exactly one instance (not all).
★ Quick Debug Cheat Sheet for Eulerian Path AlgorithmsQuick checks and commands to diagnose why Hierholzer's algorithm isn't working in your environment.
Algorithm returns empty result
Immediate action
Run degree analysis: check all degrees are even (circuit) or exactly 0/2 odd (path).
Commands
degree_check.py --graph input.json
connectivity_check.py --graph input.json
Fix now
If degrees fail, graph has no Eulerian circuit. If connectivity fails, graph is disconnected. Either way, fix the data before retrying.
Recursion limit reached+
Immediate action
Switch to iterative stack implementation immediately.
Commands
python -c "import sys; sys.setrecursionlimit(10000)"
Replace recursive function with while-loop using explicit stack
Fix now
Rewrite algorithm using iterative approach — example in Hierholzer's algorithm section above.
Only one edge left but algorithm doesn't find it+
Immediate action
Check if graph contains self-loops or multiple edges that are being removed incorrectly.
Commands
python edges_remaining.py --graph input.json --debug
Print adjacency list after each step for small graphs
Fix now
Ensure you remove only one copy of each edge, not all copies. Use multiset data structure for adjacency.
Eulerian vs Hamiltonian Paths
PropertyEulerian PathHamiltonian Path
TraversalCovers every edge exactly onceCovers every vertex exactly once
ComplexityO(E) (polynomial)NP-complete (no known polynomial algorithm)
Existence criteriaDegree conditions + connectivityNo simple necessary and sufficient condition (Dirac's/Ore's are sufficient but not necessary)
ApplicationGenome assembly, route inspectionTraveling salesman problem, scheduling
AlgorithmHierholzer's (1873)Backtracking, branch and bound, dynamic programming (Held-Karp O(2^n n^2))

Key takeaways

1
Eulerian circuit exists iff
undirected — connected + all even degrees. Directed — strongly connected + in-degree = out-degree for all vertices. Check this before running Hierholzer.
2
Eulerian path (not circuit)
exactly 2 odd-degree vertices in undirected (path goes between them), or exactly one source (+1 out) and one sink (+1 in) in directed.
3
Hierholzer O(E)
start from any vertex, walk randomly until stuck. If stuck at start and all edges used — done. Otherwise find vertex on current path with unused edges, extend there.
4
The stack-based implementation avoids the recursion depth issues of the naive recursive approach. Use it in practice.
5
Bioinformatics
genome assembly treats DNA reads as edges in a de Bruijn graph. Assembling the genome = finding an Eulerian path. This is why Euler path algorithms are in production bioinformatics pipelines.
6
In production, always validate degree conditions and connectivity before running the algorithm. Skip this and you risk silent failures or partial results.

Common mistakes to avoid

4 patterns
×

Running algorithm without checking connectivity

Symptom
Hierholzer returns empty circuit even though all vertices have even degree.
Fix
Before running the algorithm, execute a DFS from any vertex and verify that every vertex with nonzero degree is visited. If disconnected, no Eulerian circuit exists.
×

Using recursion for large graphs

Symptom
RecursionError in Python (or stack overflow in C++) during execution.
Fix
Replace the recursive implementation with the iterative stack-based version. Python's default recursion limit is 1000 — insufficient for graphs with thousands of edges.
×

Not handling multiple edges correctly

Symptom
Algorithm removes all copies of a multi-edge when it should remove only one, leading to an incorrect path or missing edges.
Fix
Ensure adjacency lists store each edge as a separate entry (e.g., list of neighbours with duplicates allowed). When popping, remove only one instance (using list.pop()) and remove the corresponding reverse edge in undirected graphs.
×

Assuming an Eulerian circuit exists in a directed graph with equal in/out degrees but weak connectivity

Symptom
Algorithm produces a path that doesn't cover all edges, or gets stuck early.
Fix
Check strong connectivity (every vertex reachable from every other) using Kosaraju or Tarjan's algorithm. Weak connectivity is insufficient for a directed Eulerian circuit.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
State the necessary and sufficient conditions for an Eulerian circuit to...
Q02JUNIOR
Why did the Königsberg bridge problem have no solution?
Q03SENIOR
Explain Hierholzer's algorithm and its time complexity.
Q04SENIOR
How does an Eulerian path problem differ from a Hamiltonian path problem...
Q05SENIOR
How would you modify Hierholzer's algorithm for a directed graph?
Q01 of 05JUNIOR

State the necessary and sufficient conditions for an Eulerian circuit to exist.

ANSWER
For an undirected graph: the graph must be connected (all vertices with nonzero degree belong to a single connected component) and every vertex must have even degree. For a directed graph: the graph must be strongly connected (or quasi-strongly connected) and every vertex must have equal in-degree and out-degree.
FAQ · 3 QUESTIONS

Frequently Asked Questions

01
What is the difference between Eulerian and Hamiltonian paths?
02
What is a de Bruijn graph and how is it used in genome assembly?
03
Can there be multiple Eulerian circuits in the same graph? How to enumerate them?
🔥

That's Graphs. Mark it forged?

9 min read · try the examples if you haven't

Previous
Maximum Flow — Ford-Fulkerson and Edmonds-Karp Algorithm
16 / 17 · Graphs
Next
Articulation Points and Bridges in Graphs