9.18 Euler Paths
You need to traverse every edge in a graph exactly once.
9.18 Euler Paths
Problem
You need to traverse every edge in a graph exactly once.
Some problems care about edges rather than vertices. A delivery route may need to use every street segment. A drawing puzzle may require tracing every line without lifting the pen. A network diagnostic tool may need to test every link. A genome assembly algorithm may need to walk through every overlap edge.
You need to determine whether such a traversal exists and, if it does, construct it.
Solution
Use Euler path conditions and Hierholzer's algorithm.
An Euler path is a path that uses every edge exactly once.
An Euler circuit is an Euler path that starts and ends at the same vertex.
Example:
A -- B
| |
D -- C
An Euler circuit exists:
A → B → C → D → A
Every edge is used once, and the path returns to the starting vertex.
Discussion
Euler paths are different from ordinary graph paths.
A standard path usually avoids repeating vertices.
An Euler path may revisit vertices many times.
The restriction is on edges:
Each edge must be used exactly once.
This changes the problem completely.
For example:
A -- B -- C
has an Euler path:
A → B → C
but not an Euler circuit, because the path starts at A and ends at C.
Undirected Euler Conditions
For an undirected graph, degrees determine whether an Euler traversal exists.
| Graph Property | Euler Result |
|---|---|
| All non-isolated vertices connected and 0 odd-degree vertices | Euler circuit |
| All non-isolated vertices connected and 2 odd-degree vertices | Euler path |
| Otherwise | No Euler path |
The odd-degree rule is the key.
In an Euler traversal, every time you enter a vertex, you usually leave it through a different unused edge. This pairs incident edges. Therefore most vertices must have even degree.
Only the start and end vertices of an open Euler path may have odd degree.
Example: Euler Circuit
Consider:
A -- B
| |
D -- C
Degrees:
| Vertex | Degree |
|---|---|
| A | 2 |
| B | 2 |
| C | 2 |
| D | 2 |
All degrees are even.
An Euler circuit exists.
Example: Euler Path
Consider:
A -- B -- C
Degrees:
| Vertex | Degree |
|---|---|
| A | 1 |
| B | 2 |
| C | 1 |
There are exactly two odd-degree vertices.
An Euler path exists, starting at one odd vertex and ending at the other.
Valid path:
A → B → C
Example: No Euler Path
Consider:
A
/|\\n B C D
Degrees:
| Vertex | Degree |
|---|---|
| A | 3 |
| B | 1 |
| C | 1 |
| D | 1 |
There are four odd-degree vertices.
No Euler path exists.
Recipe: Test Undirected Euler Type
def euler_type_undirected(graph):
odd = [
vertex
for vertex, neighbors in graph.items()
if len(neighbors) % 2 == 1
]
if len(odd) == 0:
return "circuit"
if len(odd) == 2:
return "path"
return "none"
This only checks degrees. A complete implementation must also verify connectivity among non-isolated vertices.
Connectivity Requirement
Degree conditions are not enough.
Consider:
A -- B
C -- D
Degrees:
| Vertex | Degree |
|---|---|
| A | 1 |
| B | 1 |
| C | 1 |
| D | 1 |
There are four odd vertices, so no Euler path exists.
Now consider:
A -- B -- A
C -- D -- C
Each component may satisfy local degree conditions, but no single traversal can cover both components because the graph is disconnected.
Euler traversal requires all non-isolated vertices to belong to one connected component.
Recipe: Check Non-Isolated Connectivity
def connected_non_isolated(graph):
start = None
for vertex, neighbors in graph.items():
if neighbors:
start = vertex
break
if start is None:
return True
visited = set()
def dfs(vertex):
visited.add(vertex)
for neighbor in graph[vertex]:
if neighbor not in visited:
dfs(neighbor)
dfs(start)
for vertex, neighbors in graph.items():
if neighbors and vertex not in visited:
return False
return True
Now combine the checks:
def has_euler_path_undirected(graph):
if not connected_non_isolated(graph):
return False
return euler_type_undirected(graph) != "none"
Hierholzer's Algorithm
Once the conditions are satisfied, construct the path with Hierholzer's algorithm.
The idea is:
- Follow unused edges until stuck.
- Add vertices to the output while backtracking.
- Reverse the output.
For undirected graphs, each edge must be removed from both endpoint lists.
def euler_path_undirected(graph):
graph = {
vertex: neighbors[:]
for vertex, neighbors in graph.items()
}
odd = [
vertex
for vertex, neighbors in graph.items()
if len(neighbors) % 2 == 1
]
if len(odd) == 2:
start = odd[0]
else:
start = next(
(
vertex
for vertex, neighbors in graph.items()
if neighbors
),
None
)
if start is None:
return []
stack = [start]
path = []
while stack:
vertex = stack[-1]
if graph[vertex]:
neighbor = graph[vertex].pop()
graph[neighbor].remove(vertex)
stack.append(neighbor)
else:
path.append(stack.pop())
path.reverse()
return path
Example:
graph = {
"A": ["B", "D"],
"B": ["A", "C"],
"C": ["B", "D"],
"D": ["A", "C"],
}
print(euler_path_undirected(graph))
Possible output:
['A', 'D', 'C', 'B', 'A']
Why Backtracking Builds the Path
The algorithm walks edges until it reaches a vertex with no unused outgoing edges.
At that point, the vertex must be final within the current partial route.
So it is appended to the answer.
Backtracking continues, appending vertices only when no more unused edges remain.
This postorder behavior ensures that cycles are stitched together correctly.
The final reverse produces the Euler traversal.
Directed Euler Conditions
For directed graphs, use in-degree and out-degree.
An Euler circuit exists if:
in_degree(v) == out_degree(v)
for every vertex, and all non-isolated vertices lie in one strongly connected region when directions are respected appropriately.
An Euler path exists if:
| Vertex Type | Degree Condition |
|---|---|
| Start vertex | out-degree = in-degree + 1 |
| End vertex | in-degree = out-degree + 1 |
| All others | in-degree = out-degree |
All other cases fail.
Recipe: Directed Degree Check
def directed_degrees(graph):
indegree = {
vertex: 0
for vertex in graph
}
outdegree = {
vertex: len(neighbors)
for vertex, neighbors in graph.items()
}
for source, neighbors in graph.items():
for target in neighbors:
indegree[target] += 1
return indegree, outdegree
Then:
def euler_type_directed(graph):
indegree, outdegree = directed_degrees(graph)
starts = 0
ends = 0
for vertex in graph:
diff = outdegree[vertex] - indegree[vertex]
if diff == 1:
starts += 1
elif diff == -1:
ends += 1
elif diff != 0:
return "none"
if starts == 0 and ends == 0:
return "circuit"
if starts == 1 and ends == 1:
return "path"
return "none"
This captures the degree condition for directed Euler traversal.
Applications
Route Inspection
A city wants to inspect every street segment exactly once where possible.
Edges are streets.
Vertices are intersections.
Euler paths model the desired route.
Drawing Problems
The classic question:
Can this figure be drawn without lifting the pen and without retracing a line?
is an Euler path problem.
Genome Assembly
Some genome assembly algorithms model fragments as edges in a de Bruijn graph.
Finding a sequence can become an Euler path problem.
Network Testing
A diagnostic tool may need to traverse every link.
Euler traversal gives the ideal case when it exists.
Complexity Analysis
Let:
V= number of verticesE= number of edges
For adjacency-list graphs:
| Operation | Complexity |
|---|---|
| Degree check | O(V + E) |
| Connectivity check | O(V + E) |
| Hierholzer traversal | O(V + E) with efficient edge removal |
| Memory usage | O(V + E) |
The simple Python implementation using list.remove can degrade because removal from a list is linear.
For large graphs, store edge IDs or use multisets to avoid this overhead.
Common Pitfalls
Do not confuse Euler paths with Hamiltonian paths.
Euler paths use every edge exactly once.
Hamiltonian paths visit every vertex exactly once.
Do not check only degree conditions and ignore connectivity.
Do not forget that isolated vertices do not prevent Euler traversal.
Do not use slow edge removal on large graphs.
Do not assume the Euler path is unique.
Many graphs have multiple valid Euler paths.
Do not ignore parallel edges. Euler algorithms must treat each edge occurrence separately.
Takeaway
Euler paths traverse every edge exactly once. In undirected graphs, their existence is determined by connectivity and the number of odd-degree vertices. In directed graphs, in-degree and out-degree determine the possible start and end vertices. Hierholzer's algorithm constructs the traversal efficiently and forms the standard practical method for Euler path and Euler circuit problems.