100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace

What is Kosaraju's Algorithm?

Learn how Kosaraju's algorithm finds strongly connected components using two DFS passes, and how to explain it in an interview.

hardQ59 of 227 in Data Structures & Algorithms Est. time: 6 minsLast updated:
Open Code Lab

Expected Interview Answer

Kosaraju's algorithm finds all strongly connected components (SCCs) of a directed graph in O(V + E) time using two depth-first search passes: one on the original graph to record finish times, and one on the transposed (edge-reversed) graph processed in reverse finish order.

The first pass runs DFS on the original graph and pushes each vertex onto a stack when its DFS call finishes, so vertices that finish last end up on top. The graph is then transposed by reversing every edge's direction. The second pass pops vertices off the stack one at a time and, for each unvisited vertex, runs DFS on the transposed graph; every vertex reached in that single DFS call forms exactly one strongly connected component. This works because reversing edges and processing in decreasing finish order guarantees that a DFS launched from a vertex in the transposed graph can never leak into a different SCC that finished earlier. SCCs are used to collapse cyclic dependency graphs into a DAG, detect deadlock cycles, and analyze web-link or software module structure.

  • O(V + E) linear time for full SCC decomposition
  • Conceptually simple: two DFS passes plus a transpose
  • Collapses cycles into a condensation DAG
  • Foundational for dependency and deadlock analysis

AI Mentor Explanation

Kosaraju's algorithm is like figuring out which cricket teams have mutual head-to-head rivalries, meaning each team in the group has both beaten and lost to every other team in that group at some point. First you tour every team following only "beat" arrows and note the order in which you finish exploring each team's full record, stacking the last-finished team on top. Then you flip every "beat" arrow into "lost to" and revisit teams starting from the top of that stack, and every team reachable in one such sweep forms one mutual-rivalry group. This two-pass, arrow-reversal trick is exactly why it correctly separates the cricketing world into tightly mutual rivalry clusters instead of one tangled mess.

Step-by-Step Explanation

  1. Step 1

    First DFS pass, record finish order

    Run DFS on the original graph; push each vertex to a stack when its DFS call completes.

  2. Step 2

    Transpose the graph

    Reverse the direction of every edge to build the transposed graph.

  3. Step 3

    Second DFS pass on transpose

    Pop vertices off the stack in order; for each unvisited one, run DFS on the transpose to collect one SCC.

  4. Step 4

    Build the condensation DAG

    Collapse each SCC into a single node to get an acyclic summary graph of dependencies.

What Interviewer Expects

  • Explain both DFS passes and why the graph must be transposed between them
  • State the O(V + E) linear time complexity
  • Justify why processing in decreasing finish order is correct
  • Name a use case: cycle collapsing, deadlock detection, dependency analysis

Common Mistakes

  • Forgetting to transpose the graph before the second DFS pass
  • Running the second pass in the wrong (increasing) finish-time order
  • Confusing SCCs (directed graphs) with connected components (undirected graphs)
  • Not resetting the visited set between the two DFS passes appropriately

Best Answer (HR Friendly)

โ€œKosaraju's algorithm finds tightly-connected clusters in a one-way graph, where every node in a cluster can reach every other node and get back. I do one pass to figure out a smart finishing order, reverse all the arrows, then do a second pass in that order, and each sweep in the second pass hands me one cluster.โ€

Code Example

Kosaraju's algorithm for strongly connected components
from collections import defaultdict

def kosaraju(n, edges):
    graph = defaultdict(list)
    reverse = defaultdict(list)
    for u, v in edges:
        graph[u].append(v)
        reverse[v].append(u)

    visited = [False] * n
    order = []

    def dfs1(u):
        visited[u] = True
        for v in graph[u]:
            if not visited[v]:
                dfs1(v)
        order.append(u)

    for u in range(n):
        if not visited[u]:
            dfs1(u)

    visited = [False] * n
    sccs = []

    def dfs2(u, component):
        visited[u] = True
        component.append(u)
        for v in reverse[u]:
            if not visited[v]:
                dfs2(v, component)

    for u in reversed(order):
        if not visited[u]:
            component = []
            dfs2(u, component)
            sccs.append(component)

    return sccs

Follow-up Questions

  • How does Tarjan's algorithm find SCCs differently, using only one DFS pass?
  • How would you build the condensation DAG from the discovered SCCs?
  • Why does processing in decreasing finish order guarantee correctness?
  • How would you detect a cycle using SCC results?

MCQ Practice

1. How many DFS passes does Kosaraju's algorithm perform?

It performs one DFS pass on the original graph and one on the transposed graph.

2. What must happen to the graph between the two DFS passes?

The graph is transposed, reversing every edge, before the second DFS pass runs.

3. What is the overall time complexity of Kosaraju's algorithm?

Both DFS passes and the transpose run in linear time relative to vertices and edges, giving O(V + E).

Flash Cards

What does Kosaraju's algorithm compute? โ€” All strongly connected components of a directed graph.

What is done between the two DFS passes? โ€” The graph is transposed (every edge direction reversed).

What order is used for the second DFS pass? โ€” Decreasing finish-time order from the first pass, via a stack.

What is Kosaraju's algorithm's time complexity? โ€” O(V + E), linear in vertices and edges.

1 / 4

Continue Learning