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
- 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.
Proof
Let be the root of , then, for any vertex in , applying the lemma yields that is an ancestor of in .
For any vertex on the path between and in , .
Since is strongly connected to , it follows that
Therefore, since , it follows that is also strongly connected to .
- 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.
Proof
If is an edge directed from a vertex in the subtree to an unassigned vertex outside the subtree, since is unassigned, the root of the strongly connected component containing is an ancestor of that is not .
Let denote this root, then, since is strongly connected to , it follows that
Therefore, since , it follows that is strongly connected to , which implies that is not a root.
If there is no edge directed from a vertex in the subtree to an unassigned vertex outside the subtree,
if does not have a parent, is a root.
Otherwise, let be the parent of .
If and are strongly connected, let , where and , be a path from to .
Then, since , it follows that are in the same connected component as , which implies that are all unassigned.
Since is in the subtree but is not, there exists an in such that is in the subtree but is not, which implies that is an edge directed from a vertex in the subtree to an unassigned vertex outside the subtree.
By contradiction, it follows that and are not strongly connected, which implies that is a root.
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;
}