Skip to content

Eulerian Circuit

An Eulerian circuit (or Eulerian cycle) is a path in a graph that visits every edge exactly once and returns to the starting vertex. A graph containing such a circuit is called an Eulerian graph. This is distinct from an Eulerian path, which visits every edge exactly once but doesn’t necessarily end at the starting vertex.

Importance and Problem Solving:

Eulerian circuits are crucial for solving problems involving complete traversal of edges. Real-world applications include:

  • Network routing: Finding efficient routes that cover all links in a network.
  • Robotics: Planning paths for robots to traverse all edges of a map.
  • Street sweeping: Designing routes for street sweepers to cover all streets.
  • DNA sequencing: Assembling DNA fragments.

Core Concepts and Terminology:

  • Graph: A collection of nodes (vertices) connected by edges.
  • Degree of a vertex: The number of edges incident to a vertex.
  • Connected graph: A graph where there’s a path between any two vertices.
  • Strongly connected graph (directed graphs): A directed graph where there’s a directed path between any two vertices.

2. When to Use Eulerian-circuit (and When Not To)

Section titled “2. When to Use Eulerian-circuit (and When Not To)”

Use an Eulerian circuit algorithm when you need to find a path that traverses every edge of a graph exactly once and returns to the starting point.

When NOT to use:

  • Finding shortest paths: Use Dijkstra’s algorithm or A* search instead.
  • Finding minimum spanning trees: Use Prim’s or Kruskal’s algorithm.
  • Graphs with odd-degree vertices (for circuits): An Eulerian circuit only exists if all vertices have an even degree (or exactly two vertices have odd degrees for an Eulerian path).

3. Core Algorithm / Data Structure Template

Section titled “3. Core Algorithm / Data Structure Template”

Hierholzer’s algorithm is a classic and efficient method for finding an Eulerian circuit:

  1. Choose a starting vertex. Any vertex with a non-zero degree will work.
  2. Traverse the graph: Follow a path, removing edges as you traverse them. Use a stack or recursion to keep track of the path.
  3. Handle cycles: When you reach a vertex with no more outgoing edges, add the current path to the overall circuit. If the circuit doesn’t include all edges, choose an unused edge from a vertex already in the circuit and repeat step 2.
  4. Concatenate paths: Combine the paths to form the final Eulerian circuit.

Python

from collections import defaultdict
def find_eulerian_circuit(graph):
"""Finds an Eulerian circuit in a graph using Hierholzer's algorithm."""
adj = defaultdict(list)
for u, v in graph:
adj[u].append(v)
adj[v].append(u) #Undirected graph
stack = ['JFK'] #Start at JFK
circuit = []
while stack:
u = stack[-1]
if adj[u]:
v = adj[u].pop()
adj[v].remove(u) # Remove edge from both directions
stack.append(v)
else:
circuit.append(stack.pop())
return circuit[::-1] #Reverse to get the correct order
graph = [('JFK', 'SFO'), ('JFK', 'LAX'), ('SFO', 'LAX'), ('LAX', 'JFK')]
circuit = find_eulerian_circuit(graph)
print(circuit)

Java (More complex, requires a graph representation)

import java.util.*;
public class EulerianCircuit {
public static List<String> findEulerianCircuit(Map<String, List<String>> graph) {
//Implementation similar to Python, but using Java Collections
//Would need to handle edge removal carefully using Iterators.
return null; // Placeholder - Implementation left as an exercise
}
public static void main(String[] args) {
// Example usage (requires graph creation and population)
}
}

C++ (Similar complexity to Java, requires graph representation)

#include <iostream>
#include <vector>
#include <map>
using namespace std;
// C++ implementation would be similar to Java, but using STL containers.
// Implementation left as an exercise.
int main() {
// Example Usage
return 0;
}
AlgorithmTime ComplexitySpace Complexity
Hierholzer’sO(E)O(V + E)

Where:

  • E = Number of edges
  • V = Number of vertices

The time complexity is linear in the number of edges because each edge is visited exactly once. The space complexity is linear because we need to store the adjacency list (or matrix) and the path.

  • Directed Graphs: For directed graphs, ensure the graph is strongly connected and the in-degree equals the out-degree for all vertices (except possibly two for an Eulerian path).
  • Undirected Graphs: Ensure all vertices have an even degree (except possibly two for an Eulerian path).
  • Edge Removal: Carefully remove edges from the adjacency list/matrix to avoid double counting or missing edges. Using iterators in Java or C++ is recommended.
  • Cycle Detection: The core of Hierholzer’s algorithm relies on detecting and handling cycles effectively. A stack or recursion is crucial for backtracking.
  • Lexicographical Ordering (Reconstruct Itinerary): If you need to find the lexicographically smallest solution (like in the “Reconstruct Itinerary” problem), you’ll need to adapt the algorithm to prioritize edges based on lexicographical order.

High-level approach:

The “Reconstruct Itinerary” problem can be solved using a variation of Hierholzer’s algorithm. Build a directed graph where airports are vertices and tickets represent directed edges. Start at “JFK” and follow the algorithm, prioritizing edges (flights) in lexicographical order to ensure the smallest lexical order itinerary is found. The algorithm needs to be modified to handle the case where there might be multiple outgoing edges from a vertex. You should prioritize the lexicographically smaller destination airport when choosing the next edge. This ensures the smallest lexical order itinerary is produced. A depth-first search with backtracking can also solve this effectively.