Introduction

Two classical applications of DFS finish times are topological sort of a directed acyclic graph (DAG) and the computation of strongly connected components (SCCs) of a directed graph. A topological order lists vertices so that every edge goes forward in the list. An SCC is a maximal set of vertices that are mutually reachable. Both of these two algorithms run in Θ(V+E)\Theta(V+E) time and are standard GATE CSE graph-algorithm questions.

Prerequisites

Core concepts

Topological order

A topological order of a directed graph G=(V,E)G=(V,E) is a linear order of VV such that for every edge u→vu\to v, vertex uu appears before vv. Such an order exists if and only if GG is a DAG (equivalently: DFS finds no back edge).

Strong connectivity

Vertices uu and vv are mutually reachable when uu can reach vv and vv can reach uu. Mutual reachability is an equivalence relation; its classes are the strongly connected components. The component graph (condensation) has one vertex per SCC and an edge Ci→CjC_i\to C_j only for Ci≠CjC_i\ne C_j, when some edge of GG goes from CiC_i to CjC_j. The component graph is always a DAG.

Kosaraju's method (CLRS)

  1. Run DFS on GG and record finish times.
  2. Compute the transpose GTG^T (reverse every edge).
  3. Run DFS on GTG^T, considering vertices in order of decreasing finish time from step 1. Each tree in this second forest is one SCC.

Algorithms and pseudocode

Pseudocode uses adjacency lists, <- for assignment and the DFS routines of the previous note.

Topological sort via DFS

Text
TOPOLOGICAL-SORT(G)
1  call DFS(G) to compute finish times
2  as each vertex is finished, insert it at the front of a linked list
3  return the linked list

Equivalent view and costs. Emit vertices in decreasing finish-time order. Use adjacency lists and constant-time vertex, edge, stack and queue operations; VV and EE denote their counts in cost bounds. On a nonempty DAG the complete DFS takes Θ(V+E)\Theta(V+E) time in best, average and worst cases, with O(V)O(V) auxiliary marks/stack excluding the input graph and Θ(V)\Theta(V)-vertex output order. If a back edge appears, abort: no topological order exists. With the usual eager initialization of all vertex marks, a cyclic input can abort in Θ(V)\Theta(V) best time and Θ(V+E)\Theta(V+E) worst time; an average needs an input/adjacency-order distribution. The empty traversal takes Θ(1)\Theta(1).

Kahn's algorithm (indegree reduction)

Related material. Repeatedly emit a vertex of indegree 0 and delete its outgoing edges. With a queue of current indegree-0 vertices the algorithm runs in Θ(V+E)\Theta(V+E). If fewer than ∣V∣|V| vertices are emitted, the graph has a cycle. Kahn's method is presented alongside the DFS method and is not a separate syllabus item. Build indegrees by a full adjacency-list pass. Under the preceding model this costs Θ(V+E)\Theta(V+E) in best, average and worst cases, even if a cycle later stops the queue; auxiliary indegrees/queue use O(V)O(V) words, excluding the input graph and output order. The graph need not be copied or physically deleted. Empty input costs Θ(1)\Theta(1).

Text
KAHN(G)
1  for each v in G.V: indeg[v] <- number of incoming edge occurrences at v (including multiplicity)
2  Q <- queue of all v with indeg[v] = 0
3  order <- empty list
4  while Q is not empty
5      u <- DEQUEUE(Q); append u to order
6      for each v in Adj[u]
7          indeg[v] <- indeg[v] - 1
8          if indeg[v] = 0 then ENQUEUE(Q, v)
9  if |order| = |V| then return order else return "cycle"

Strongly connected components

Text
STRONGLY-CONNECTED-COMPONENTS(G)
1  call DFS(G) to compute finish times
2  compute G^T
3  call DFS(G^T), but in the main loop of DFS consider vertices
   in order of decreasing finish time from step 1
4  output the vertices of each tree in the DFS forest of step 3 as one SCC

Time and space. Two complete DFS passes and one transpose take Θ(V+E)\Theta(V+E) time in best, average and worst cases on nonempty adjacency-list graphs under the stated model. Reuse the recorded reverse finish order rather than comparison-sorting timestamps. Marks, finish order and recursion stacks require O(V)O(V) words; an explicitly stored transpose adds Θ(V+E)\Theta(V+E) words, so total auxiliary storage is Θ(V+E)\Theta(V+E), excluding the input graph and SCC output. Empty input costs Θ(1)\Theta(1).

How to build the transpose

The transpose GTG^T has the same vertex set and an edge v→uv\to u for every edge u→vu\to v of GG. On adjacency lists, allocate empty lists for every vertex and, for each uu and each v∈Adj[u]v\in Adj[u], append uu to AdjT[v]Adj^T[v]. That pass is Θ(V+E)\Theta(V+E) and produces a representation ready for the second DFS. Its explicit output representation uses Θ(V+E)\Theta(V+E) words; the construction itself needs O(1)O(1) working words beyond that output and the input graph. These bounds hold in every case for a nonempty graph under the same word-operation model; the empty case is constant.

Counting paths in a DAG

Related material. Once a topological order is known, the number of paths from ss to tt in a DAG fills in linear time: initialize count[v]=0 for all vertices and then count[s]=1; process vertices in topological order, add count[u] into count[v] for every edge u->v, and return count[t]. The recurrence is ordinary DP on the DAG; a problem may ask for path counts rather than the order itself (CLRS Exercise 22.4-2). For a nonempty DAG and valid s,ts,t, the full pass performs Θ(V+E)\Theta(V+E) unit-cost counter/edge operations in best, average and worst cases and uses Θ(V)\Theta(V) auxiliary counters, excluding the graph, supplied order and scalar result. This is an arithmetic-operation model, not an arbitrary-precision bit bound. If B≥1B\ge1 is the maximum bit length of any intermediate count, the additions take at most O(EB)O(EB) bit operations and the counter array at most O(VB)O(VB) bits; graph/index operations are separate.

Unique topological order

A DAG has a unique topological order if and only if it has a Hamiltonian path that is forced by edges (equivalently: the topo order is unique iff there is an edge between every pair of consecutive vertices in that order). Checking uniqueness after Kahn's algorithm is easy: the indegree-0 queue must never hold more than one vertex.

Counting topological orders

To count all topological orders, work from the structure: vertices with no path between them in either direction can be interleaved freely, subject to the remaining edges.

  • nn vertices and no edges: n!n! orders. A directed path through all nn vertices: exactly 1.
  • Edges a→ca\to c, b→cb\to c, c→dc\to d, c→ec\to e: aa and bb come first in either order, then cc, then dd and ee in either order, so 2×1×2=42 \times 1 \times 2 = 4 orders.
  • The DAG of the worked example below has exactly 2 orders, a,b,c,d,ea,b,c,d,e and a,c,b,d,ea,c,b,d,e: dd must follow both bb and cc, and ee must follow dd, so only bb and cc can swap.

For a general small DAG, count recursively: the number of orders equals the sum, over each current source vv, of the number of orders of the DAG with vv removed. With a min-priority queue in place of Kahn's FIFO queue, the algorithm outputs the lexicographically smallest topological order.

Mathematical explanation

Why decreasing finish time works for topological sort

In a DAG every edge u→vu\to v satisfies f[u]>f[v]f[u] > f[v] (CLRS 3e, Section 22.4): otherwise vv would still be grey when uu examines the edge, making it a back edge and creating a cycle. Sorting by decreasing ff therefore places uu before vv.

Why Kosaraju works (sketch)

For an SCC CC, let f(C)f(C) be the largest finish time of any vertex of CC in the first DFS. If the component graph has an edge C→C′C \to C', then f(C)>f(C′)f(C) > f(C') (CLRS 3e, Section 22.5). So ordering components by decreasing f(C)f(C) is a topological order of the component graph. The second DFS starts at the vertex with the largest finish time, which lies in a source component CC of the component graph. In GTG^T every inter-component edge is reversed, so from CC the search can leave only into components that are already finished, and the tree grown is exactly CC. Repeating the argument for each new start gives one tree per SCC.

Why the transpose is needed. Running the second pass on GG itself fails, whether the vertices are taken in decreasing or in increasing finish order (CLRS Exercise 22.5-3 asks about the increasing order). In the five-vertex SCC example below, decreasing finish order starts the second pass at aa and increasing finish order starts it at cc; in both cases DFS on GG reaches all five vertices, wrongly merging the two components.

Worked examples

Topological order of a small DAG

Vertices a,b,c,d,ea,b,c,d,e. Edges: a→ba\to b, a→ca\to c, b→db\to d, c→dc\to d, c→ec\to e, d→ed\to e. One DFS (outer order a,b,c,d,ea,b,c,d,e; adjacency lists alphabetical) runs as follows: discover aa (1), bb (2), dd (3), ee (4); finish ee (5), dd (6), bb (7); discover cc (8), whose edges c→dc\to d and c→ec\to e reach black vertices (cross edges); finish cc (9), aa (10). Discovery/finish times: a:1/10a: 1/10, b:2/7b: 2/7, c:8/9c: 8/9, d:3/6d: 3/6, e:4/5e: 4/5. Decreasing finish: a,c,b,d,ea,c,b,d,e. The only other valid order is a,b,c,d,ea,b,c,d,e, which Kahn's algorithm with a FIFO queue produces (queue contents: aa; then b,cb, c; then cc; then dd; then ee). Both respect every edge.

DAG with one topological order highlighted
Figure 1. The DAG on {a,b,c,d,e}\{a,b,c,d,e\} with edges a→ba\to b, a→ca\to c, b→db\to d, c→dc\to d, c→ec\to e, d→ed\to e: one topological order is a,c,b,d,ea,c,b,d,e.

SCCs on five vertices

Vertices a,b,c,d,ea,b,c,d,e. Edges: a→ba\to b, b→cb\to c, c→ac\to a, b→db\to d, d→ed\to e, e→de\to d. SCCs: {a,b,c}\{a,b,c\} and {d,e}\{d,e\}. The component graph is {a,b,c}→{d,e}\{a,b,c\}\to\{d,e\}.

First DFS (outer order a,b,c,d,ea,b,c,d,e; lists a:ba: b, b:c,db: c, d, c:ac: a, d:ed: e, e:de: d): discover aa (1), bb (2), cc (3); c→ac\to a is a back edge; finish cc (4); discover dd (5), ee (6); e→de\to d is a back edge; finish ee (7), dd (8), bb (9), aa (10). Finish order c,e,d,b,ac, e, d, b, a; decreasing finish order a,b,d,e,ca, b, d, e, c.

Second DFS on GTG^T (edges b→ab\to a, c→bc\to b, a→ca\to c, d→bd\to b, e→de\to d, d→ed\to e) in the order a,b,d,e,ca, b, d, e, c: from aa it reaches cc and then bb, and the edge back to aa is already used, giving the tree {a,b,c}\{a, b, c\}. The next unvisited vertex in the order is dd; its edge to bb leads to a visited vertex, and it reaches ee, giving {d,e}\{d, e\}. Here f({a,b,c})=10>f({d,e})=8f(\{a,b,c\}) = 10 > f(\{d,e\}) = 8, matching the component edge {a,b,c}→{d,e}\{a,b,c\}\to\{d,e\}.

SCCs and their condensation DAG
Figure 2. Digraph with SCCs {a,b,c}\{a,b,c\} and {d,e}\{d,e\}: the condensation is the two-node DAG {a,b,c}→{d,e}\{a,b,c\}\to\{d,e\}.

Edge cases and implementation notes

  • A zero-vertex graph has the empty topological order and no SCCs. A singleton without a self-loop has the one-vertex order and one SCC. A self-loop prevents a topological order; the vertex still belongs to an SCC.
  • A complete directed cycle of nn vertices is one SCC and has no topological order.
  • Parallel edges and self-loops: a self-loop is a back edge (cycle); parallel edges do not change reachability, but Kahn counts and decrements indegree once per edge occurrence.
  • When adjacency lists are stored only for outgoing edges, building GTG^T must create entries for vertices that had indegree but no outdegree in GG.
  • Stable sorting of finish times is unnecessary: any total order that respects the numeric finish values works for Kosaraju's second pass.

Comparing the two topological algorithms in an exam answer: DFS needs recursion (or an explicit stack) and produces one valid order tied to adjacency order; Kahn's algorithm makes indegrees explicit and naturally detects cycles by counting emitted vertices. Both are Θ(V+E)\Theta(V+E).

Important properties and observations

  • Topological orders are not unique when incomparable vertices exist.
  • Adding an edge can merge SCCs (never split them); the number of SCCs never increases (CLRS Exercise 22.5-1). One new edge can merge many components at once: on a directed path v1→v2→⋯→vnv_1 \to v_2 \to \cdots \to v_n (nn singleton SCCs), adding vn→v1v_n \to v_1 leaves a single SCC.
  • The transpose GTG^T has the same SCCs as GG.
  • A single-vertex SCC is possible even when the vertex has in- and out-edges (they leave the component).

Common misconceptions.

  • "Any DFS order is a topological order." Only decreasing finish time (or Kahn's emission order) works; discovery order does not.
  • "SCCs can be found by one DFS." One DFS finds trees, not SCCs; Kosaraju needs the transpose pass. Tarjan's single-pass algorithm exists but is beyond this note.
  • "The second Kosaraju pass can run on GG." It must run on GTG^T; on GG it merges components.
  • "The vertex finishing last in a DAG's DFS is a sink." It is a source (it comes first in the topological order); the vertex finishing first is a sink.
  • "The component graph may have cycles." By construction it is a DAG.

GATE examination perspective

  • EMIT BY FINISH TIME. List vertices by decreasing f[u]f[u].
  • DETECT A CYCLE. A back edge during the topo DFS, or Kahn emitting fewer than nn vertices.
  • NAME THE SCCs. Trace Kosaraju's two passes on a small digraph.
  • CONDENSATION. Draw the component DAG after identifying SCCs.
  • COMPLEXITY. Both algorithms Θ(V+E)\Theta(V+E).
  • COUNT THE ORDERS. Group the vertices that are forced into positions and multiply the free interleavings, or remove sources recursively on a small DAG.
  • KAHN VERSUS DFS. Kahn with a FIFO queue and DFS by decreasing finish time can output different valid orders; check every option against every edge.

Summary

Sources

  1. T. H. Cormen, C. E. Leiserson, R. L. Rivest, C. Stein, Introduction to Algorithms, 3rd ed., MIT Press, 2009: Chapter 22, Sections 22.4 (topological sort) and 22.5 (strongly connected components); Exercises 22.4-2 (counting paths), 22.5-1 (adding an edge) and 22.5-3 (second pass on G).

Next steps