Tarjan’s Strongly Connected Components Algorithm is an algorithm that finds the strongly connected components of a directed graph in and space.

Tip

This problem can also be solved by Kosaraju’s Algorithm in time and space.

Algorithm

  1. Find an arbitrary depth-first search forest of .

Lemma

Call the vertex in a strongly connected component that is visited first during the depth-first search the root of , then, in , all the vertices in are in the subtree rooted at the root of .

Lemma

For any strongly connected component of , is connected.

  1. During the depth-first search, if is a root, after processing its subtree, assign all the vertices in its subtree that have not been assigned to any strongly connected component to the current component. Applying the lemma yields that the current component is the strongly connected component rooted at .

Lemma

For any vertex in , is a root iff, after processing its subtree, there is no edge in directed from a vertex in the subtree to an unassigned vertex outside the subtree.

Lemma

Let denote the entry time of during the depth-first search, denote, after processing the subtree rooted at , the minimum of an unassigned vertex reachable from a vertex in the subtree via at most one edge in .

Then, for any vertex in , is a root iff .

Applying the lemma to check if is a root yields an algorithm that solves the problem in time and space.

std::vector<std::vector<int>> tarjan(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 in(n, -1);
	int t = 0;
	std::vector<int> stk;
	std::vector<std::vector<int>> res;
	std::vector vis(n, false);
 
	for (int i = 0; i < n; i++) {
		if (~in[i]) {
			continue;
		}
 
		y_combinator([&](auto &&self, int u) -> int {
			int low = in[u] = t++;
			stk.push_back(u);
 
			for (int v : adj[u]) {
				if (in[v] == -1) {
					low = std::min(low, self(v));
				} else if (!vis[v]) {
					low = std::min(low, in[v]);
				}
			}
 
			if (low == in[u]) {
				res.emplace_back();
 
				int v;
				do {
					v = stk.back();
					stk.pop_back();
 
					res.back().push_back(v);
					vis[v] = true;
				} while (v != u);
			}
 
			return low;
		})(i);
	}
 
	return res;
}