← All Posts
DSA · Graphs · Tarjan’s SCC

Tarjan’s SCC Algorithm

Find every strongly connected component in a single DFS pass.

Why Tarjan’s SCC Algorithm?

A Strongly Connected Component (SCC) of a directed graph is a maximal set of vertices such that every vertex is reachable from every other vertex in the set. Decomposing a graph into its SCCs is fundamental to problems in dependency analysis, compiler optimisation, and competitive programming.

Tarjan’s algorithm, published in 1972, finds all SCCs in a single depth-first search. Compared to Kosaraju’s two-pass approach, Tarjan’s requires only one DFS traversal and uses an explicit stack to track which vertices might belong to the current SCC.

The Algorithm

Tarjan’s algorithm maintains two arrays and a stack:

Tarjan’s SCC
timer = 0

function dfs(u):
    disc[u] = low[u] = timer++
    stack.push(u);  onStack[u] = true

    for each neighbor v of u:
        if disc[v] == -1:          // tree edge
            dfs(v)
            low[u] = min(low[u], low[v])
        else if onStack[v]:        // back edge
            low[u] = min(low[u], disc[v])

    if disc[u] == low[u]:          // u is SCC root
        pop vertices from stack until u is popped
        → these vertices form one SCC

Key insight: A vertex u is the root of its SCC exactly when disc[u] == low[u]. At that point every vertex above u on the stack belongs to the same SCC. We pop them all off and record the component.

Back-Edge Detection

When we encounter an edge u → v where v is already on the stack, we know there is a path from v back to u (through the DFS tree) and from u to v (the current edge). This cycle means u and v are in the same SCC, so we update low[u] = min(low[u], disc[v]).

Step-by-Step Walkthrough

Trace Tarjan’s algorithm on this 8-node directed graph. The three SCCs are {0, 1, 2}, {3, 4, 5}, and {6, 7}.

0→1 2→3 5→6 0 1 2 3 4 5 6 7

Dashed arrows are cross-edges between SCCs. Solid arrows form cycles within each SCC.

Initialise

disc = [-1,-1,-1,-1,-1,-1,-1,-1], low same. Stack empty. Timer = 0.

Visit node 0

disc[0]=0, low[0]=0. Push 0 onto stack.

Stack: [0]

Visit node 1 via 0 → 1

disc[1]=1, low[1]=1. Push 1.

Stack: [0, 1]

Visit node 2 via 1 → 2

disc[2]=2, low[2]=2. Push 2.

Stack: [0, 1, 2]

Edge 2 → 0 — back edge!

Node 0 is on the stack. low[2] = min(2, disc[0]) = 0.

This tells us node 2 can reach as far back as node 0 through a cycle.

low[2] updated: 0

Visit node 3 via 2 → 3 (cross-edge to new SCC)

disc[3]=3, low[3]=3. Push 3.

Stack: [0, 1, 2, 3]

Visit node 4 via 3 → 4

disc[4]=4, low[4]=4. Push 4.

Stack: [0, 1, 2, 3, 4]

Visit node 5 via 4 → 5

disc[5]=5, low[5]=5. Push 5.

Stack: [0, 1, 2, 3, 4, 5]

Edge 5 → 3 — back edge!

Node 3 is on the stack. low[5] = min(5, disc[3]) = 3.

low[5] updated: 3

Visit node 6 via 5 → 6

disc[6]=6, low[6]=6. Push 6.

Stack: [0, 1, 2, 3, 4, 5, 6]

Visit node 7 via 6 → 7

disc[7]=7, low[7]=7. Push 7.

Stack: [0, 1, 2, 3, 4, 5, 6, 7]

Edge 7 → 6 — back edge & SCC {6, 7} found!

low[7] = min(7, disc[6]) = 6. Backtrack to node 6: low[6] = min(6, low[7]) = 6.

Since disc[6] == low[6] == 6, node 6 is an SCC root. Pop stack until 6: {7, 6}.

SCC found: {6, 7}

Backtrack — SCC {3, 4, 5} found!

Back to 5: low[5] = 3 (unchanged). Back to 4: low[4] = min(4, 3) = 3. Back to 3: low[3] = min(3, 3) = 3.

disc[3] == low[3] == 3 → node 3 is SCC root. Pop: {5, 4, 3}.

SCC found: {3, 4, 5}

Backtrack — SCC {0, 1, 2} found!

Back to 2: low[2] = 0 (unchanged). Back to 1: low[1] = min(1, 0) = 0. Back to 0: low[0] = min(0, 0) = 0.

disc[0] == low[0] == 0 → node 0 is SCC root. Pop: {2, 1, 0}.

SCC found: {0, 1, 2} — All done!

Tarjan vs Kosaraju

Tarjan’s Algorithm

  • Single DFS pass
  • Uses an explicit stack + onStack array
  • Emits SCCs as soon as root is identified
  • Slightly more complex to implement

Kosaraju’s Algorithm

  • Two DFS passes (original + transposed graph)
  • Requires building the reversed graph
  • Conceptually simpler — finish-time ordering
  • Extra O(V + E) space for the transposed graph
PropertyTarjanKosaraju
DFS passes12
Extra graph?NoYes (transposed)
TimeO(V+E)O(V+E)
Space overheaddisc + low + stackTransposed adj list
SCC output orderReverse topologicalTopological
ImplementationModerateEasy

Both run in O(V + E). Choose Tarjan’s when you want a single pass or need SCCs in reverse topological order. Choose Kosaraju’s when simplicity matters or you already have the transposed graph.

Interactive Animation

Tarjan’s SCC — Live

0 1 2 3 4 5 6 7
01234567
disc--------
low--------

Stack: [ ]

Press Play or Step to begin.

Colour key: ● Default   ● On stack   ● Current   ■ SCC found

Condensation Graph (DAG of SCCs)

Once all SCCs are found, contract each SCC into a single super-node. The resulting graph is a DAG (directed acyclic graph) called the condensation. Every edge in the original graph that crosses SCC boundaries becomes an edge in the DAG.

{0,1,2} SCC₀ {3,4,5} SCC₁ {6,7} SCC₂

The condensation is useful for:

C++ Implementation

Tarjan’s SCC

#include <bits/stdc++.h>
using namespace std;

struct TarjanSCC {
    int n, timer = 0;
    vector<vector<int>> adj;
    vector<int> disc, low, stk;
    vector<bool> onStk;
    vector<vector<int>> sccs;

    TarjanSCC(int n) : n(n), adj(n), disc(n, -1),
                       low(n), onStk(n, false) {}

    void addEdge(int u, int v) { adj[u].push_back(v); }

    void dfs(int u) {
        disc[u] = low[u] = timer++;
        stk.push_back(u);
        onStk[u] = true;

        for (int v : adj[u]) {
            if (disc[v] == -1) {
                dfs(v);
                low[u] = min(low[u], low[v]);
            } else if (onStk[v]) {
                low[u] = min(low[u], disc[v]);
            }
        }

        if (disc[u] == low[u]) {
            vector<int> scc;
            while (true) {
                int v = stk.back(); stk.pop_back();
                onStk[v] = false;
                scc.push_back(v);
                if (v == u) break;
            }
            sccs.push_back(scc);
        }
    }

    void findSCCs() {
        for (int i = 0; i < n; i++)
            if (disc[i] == -1) dfs(i);
    }
};

Condensation Graph Builder

// Returns {condensation DAG adjacency, comp[v] = SCC index of v}
pair<vector<vector<int>>, vector<int>>
buildCondensation(TarjanSCC& t) {
    int k = t.sccs.size();
    vector<int> comp(t.n);
    for (int i = 0; i < k; i++)
        for (int v : t.sccs[i])
            comp[v] = i;

    vector<vector<int>> dag(k);
    set<pair<int,int>> seen;
    for (int u = 0; u < t.n; u++)
        for (int v : t.adj[u])
            if (comp[u] != comp[v])
                if (seen.insert({comp[u], comp[v]}).second)
                    dag[comp[u]].push_back(comp[v]);

    return {dag, comp};
}

Usage Example

int main() {
    int n, m; cin >> n >> m;
    TarjanSCC g(n);
    while (m--) {
        int u, v; cin >> u >> v;
        g.addEdge(u, v);
    }
    g.findSCCs();

    // Print SCCs
    for (auto& scc : g.sccs) {
        for (int v : scc) cout << v << ' ';
        cout << '\n';
    }

    // Build and use condensation
    auto [dag, comp] = buildCondensation(g);
    // dag is a DAG — apply topological sort, DP, etc.
}

Complexity Analysis

AspectComplexityReason
TimeO(V + E)Each vertex and edge visited exactly once in DFS
SpaceO(V + E)Adjacency list + disc/low/stack arrays
CondensationO(V + E)Single pass over all edges to build the DAG

Every vertex is pushed and popped from the stack exactly once, and each edge is examined once during the DFS. The low-link updates are O(1) per edge. This makes Tarjan’s algorithm optimal for SCC decomposition.

Practical note: The recursive DFS can overflow the call stack for very large graphs (V > 106). In competitive programming, either increase the stack size or convert to an iterative implementation.

Practice Problems

  1. CSES. Planets and Kingdoms — Direct SCC decomposition
  2. CSES. Coin Collector — SCC condensation + DP on DAG
  3. Codeforces 427C. Checkposts — Min-cost vertex per SCC
  4. LeetCode 1192. Critical Connections in a Network — Tarjan’s bridges variant
  5. LeetCode 2360. Longest Cycle in a Graph — Cycle detection via SCC ideas
  6. Codeforces 22E. Scheme — Make a graph strongly connected

Summary

← Topological Sort Bridges & Articulation Points →