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

  1. 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.

  1. Let denote the circuit. After removing the edges in from , find an Eulerian circuit in each weakly connected component recursively.
  2. 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;
}