Kosaraju algorithm is mainly phrased as two recursive subroutines running postorderDFS twice to mark SCCs with linear time complexity O(V+E) below,
For each vertex \$u\$ of the graph, mark \$u\$ as unvisited. Let \$L\$ be empty.
For each vertex \$u\$ of the graph do Visit(\$u\$), where Visit(\$u\$) is the recursive subroutine:
If \$u\$ is unvisited then:
1. Mark \$u\$ as visited.
2. For each out-neighbour \$v\$ of \$u\$, do Visit(\$v\$).
3. Prepend \$u\$ to \$L\$.
Otherwise do nothing.For each element \$u\$ of \$L\$ in order, do Assign(\$u\$,\$u\$) where Assign(\$u\$,\$root\$) is the recursive subroutine:
If \$u\$ has not been assigned to a component then:
1. Assign \$u\$ as belonging to the component whose root is \$root\$.
2. For each in-neighbour \$v\$ of \$u\$, do Assign(\$v\$,\$root\$).
Otherwise do nothing.
Here is the recursive implementation in Python according to the above recipe,
def kosaraju(G): # postorder DFS on G to transpose the graph and push root vertices to L N = len(G) T, L, U = [[] for _ in range(N)], [], [False] * N def visit(u): if not U[u]: U[u] = True for v in G[u]: visit(v) T[v].append(u) L.append(u) for u in range(N): visit(u) # postorder DFS on T to pop root vertices from L and mark SCCs C = [None] * N def assign(u, r): if U[u]: U[u] = False C[u] = r for v in T[u]: assign(v, r) while L: u = L.pop() assign(u, u) return C
The following iterative implementation scales well against the stack overflow due to excessively deep recursion. I revised the inner loop of the first iterative DFS so the linear time complexity O(V+E) is guaranteed now, however it deserves to be shared for further improvement.
I'll be glad about all your opinions or alternative implementations.
def kosaraju(G): # postorder DFS on G to transpose the graph and push root vertices to L N = len(G) T, L, U = [[] for _ in range(N)], [], [False] * N for u in range(N): if not U[u]: U[u], S = True, [u] while S: u, done = S[-1], True for v in G[u]: T[v].append(u) if not U[v]: U[v], done = True, False S.append(v) break if done: S.pop() L.append(u) # postorder DFS on T to pop root vertices from L and mark SCCs C = [None] * N while L: r = L.pop() S = [r] if U[r]: U[r], C[r] = False, r while S: u, done = S[-1], True for v in T[u]: if U[v]: U[v] = done = False S.append(v) C[v] = r break if done: S.pop() return C
Test example:
G = [[1], [0, 2], [0, 3, 4], [4], [5], [6], [4], [6]] print(kosaraju(G)) # => [0, 0, 0, 3, 4, 4, 4, 7]