Abstract
Two vertices and in a directed graph are strongly connected if there exists a path from to and a path from to . The maximal set of strongly connected vertices is called a Strongly Connected Component (SCC). Every directed graph is a DAG of its SCCs, so some SCCs are sink SCCs and some are source SCCs.
- Category: Graph Algorithm (directed graphs)
- Input: Directed graph
- Output: A partition of into its strongly connected components
- Paradigm: Two-pass DFS, using a reversed graph
- Typical use cases: condensing a directed graph into its DAG-of-SCCs structure, dependency/cycle analysis
Core Logic (High-Level)
There is a linear-time algorithm that decomposes a directed graph into its SCCs. If Explore is performed on a vertex , it visits only the vertices reachable from . If is in a sink SCC, running explore on reaches exactly the nodes in that SCC — nothing more, since a sink SCC has no outgoing edges to other SCCs.
This suggests a way to look for SCCs:
- Start
exploreon a vertex in a sink SCC and visit its SCC. - Remove the sink SCC from the graph and repeat.
Key Idea
The problem is finding a vertex in a sink SCC in the first place — there’s no direct way to spot one. But there is a direct way to find a vertex in a source SCC (see Proof below), and a sink of is just a source of the reversed graph . So: run DFS on to find sink SCCs of , then use that order to peel off SCCs of one at a time.
Pseudocode (Mid-Level Implementation)
Algorithm 40 SCC
Input: Directed graph
Output: Partition of into strongly connected components
procedure SCC()
Construct , the reverse of
Run DFS on , recording for every vertex
Run DFS on , considering vertices in decreasing order of from the previous step
Note
Every time DFS increments , a new SCC has been found
Variables & Data Structures
| Name | Type | Purpose |
|---|---|---|
G^R | Graph (reversed adjacency) | Same vertices as , every edge direction flipped; sources of = sinks of |
post(v) | Array (vertex → int) | Finish timestamp from the first DFS, run on ; determines processing order for the second DFS |
cc | Integer counter | Incremented once per explore call in the second DFS — each increment marks the discovery of one new SCC |
Helper Functions / Operations Used
- DFS /
explore— see Depth First Search (DFS) and Explore for the underlying traversal andposttimestamp mechanics used here - Reverse graph construction — build by flipping every edge ;
Proof of Correctness
Property (Generalized)
Claim: If and are strongly connected components and there is an edge from a vertex in to a vertex in , then the highest post number in is greater than the highest post number in .
Case 1: DFS searches before . At some point DFS will cross into and visit every vertex in , then retrace its steps back to the first node in it started with, assigning that node the highest post number in only after all of has already been finished.

- : the last vertex removed from
visited(highest post number) - : a vertex popped before does
Case 2: DFS searches before . DFS will visit all vertices of before getting stuck (since a sink-ward SCC like here can’t lead back into ) and assign post numbers to all of . Only later does it visit some vertex of and assign post numbers to those vertices — so (in ) is popped first, and (in ) is pushed and popped later, giving .
Either way, the highest post number belongs to a vertex in , confirming the claim.
Conclusion: Linearization
The SCCs can be linearized by arranging them in decreasing order of their highest post numbers. In particular:
The vertex with the greatest post number in any [DFS Output Tree](Explore#DFS Output Tree) belongs to a source SCC.
This follows directly from the Property above: a source SCC has no incoming edges from any other SCC, so no other component’s highest post number can exceed its own.
Finding Sink SCCs via the Reverse Graph
Given , let be its reverse. Then the sources of are the sinks of . So:
- Running DFS on and taking the vertex with the highest post number gives a vertex in a source of — which is a sink of .
- Start
exploreon this vertex (in ) to find that whole SCC. - The vertex with the next-greatest post number in (among vertices not yet visited) is in the next SCC in linear order — repeat from there.
This is exactly the peeling process from Core Logic, made concrete: the second DFS (on , run in decreasing order of ‘s post numbers) never “leaks” from one SCC into an unprocessed one, because by the time it reaches a given vertex, every SCC that could pull it further has already been fully explored and marked visited in an earlier explore call.
Time & Space Complexity Analysis
General Case
| Complexity | Notes | |
|---|---|---|
| Time | Three linear-time passes, summed: |
- Construct →
- DFS on →
- DFS on →
Therefore the total time complexity of the SCC algorithm is linear: .
| Complexity | Notes | |
|---|---|---|
| Space | Adjacency lists for both and , plus the post array and visited/cc bookkeeping from the two DFS passes |
Best / Worst / Average Case
- Best / Worst / Average case: All — every step is a full graph traversal or construction; there’s no early exit, since the algorithm must find every SCC, not just one.
Drawbacks / Constraints
- Only meaningful for directed graphs. Strong connectivity collapses to ordinary connectivity on an undirected graph, so this algorithm’s structure (source/sink SCCs, reverse graph) is specific to directed inputs.
- The naive approach doesn’t work: the vertex with the least post number in a single DFS output tree on does not necessarily belong to a sink SCC — this is why the reverse-graph trick (finding sources of , i.e. sinks of ) is needed instead of trying to read sinks directly off one DFS pass on .
- Requires building explicitly (or an equivalent reverse-adjacency structure), adding extra space beyond just itself.