Hierholzer’s Algorithm is an algorithm that finds an Eulerian circuit in a weakly connected directed graph , where the in-degree of each vertex equals its out-degree, in time and space.
Algorithm 0
- Starting from an arbitrary vertex, walk by arbitrarily choosing an unvisited edge until the current vertex has no unvisited edges left.
Lemma
This process finds a circuit.
Proof
Let be the trail.
If , let be the number of occurrences of in , then the number of visited edges directed toward is , and the number of visited edges directed away from is .
Let denote the in-degree of , denote the out-degree of . Then, since there are visited edges directed toward , . On the other hand, since has no unvisited edges left in the end, .
Therefore,
By contradiction, it follows that .
- Let denote the circuit. After removing the edges in from , find an Eulerian circuit in each weakly connected component recursively.
- Joining these Eulerian circuits into yields an Eulerian circuit in .
This algorithm solves the problem in time and space.
std::vector<int> hierholzer(int n, int m, const std::vector<int> &u, const std::vector<int> &v) {
std::vector<std::vector<int>> adj(n);
for (int i = 0; i < m; i++) {
adj[u[i]].push_back(v[i]);
}
std::vector<int> res;
y_combinator([&](auto &&self, int u) -> void {
std::vector<int> c;
y_combinator([&](auto &&self, int u) -> void {
c.push_back(u);
if (!adj[u].empty()) {
int v = adj[u].back();
adj[u].pop_back();
self(v);
}
})(u);
if (c.size() == 1) {
res.push_back(u);
} else {
for (int v : c) {
self(v);
}
}
})(0);
return res;
}Algorithm 1
Based on Algorithm 0, instead of storing the circuit explicitly, recursing during the backtracking after the circuit is found yields an algorithm that solves the problem in time and space.
std::vector<int> hierholzer(int n, int m, const std::vector<int> &u, const std::vector<int> &v) {
std::vector<std::vector<int>> adj(n);
for (int i = 0; i < m; i++) {
adj[u[i]].push_back(v[i]);
}
std::vector<int> res;
y_combinator([&](auto &&self, int u) -> void {
while (!adj[u].empty()) {
int v = adj[u].back();
adj[u].pop_back();
self(v);
}
res.push_back(u);
})(0);
std::ranges::reverse(res);
return res;
}