DFS edge types, topological sort and SCC
What a depth-first search of a directed graph reveals: the four kinds of edges (tree, back, forward, cross) decided by colours and times, that a directed graph has a cycle exactly when there is a back edge, topological sorting of a DAG by decreasing finish time and by Kahn's algorithm (and when the order is unique), and strongly connected components by Kosaraju's two searches.
Why it exists
A depth-first search of a directed graph does more than visit the vertices: the kind of every edge it meets carries information. A back edge means a cycle; no back edge means the graph is a DAG (directed acyclic graph), and then the order in which the vertices finish gives a topological order, the way to schedule tasks that depend on each other. Running the search twice, on the graph and on its reversal, splits any graph into its strongly connected components. These three results (cycle detection, topological sort, SCC) are standard GATE material.
Prerequisites
You need depth-first search with colours and discovery and finish times (BFS and DFS).
Precise definitions
Directed graph. Edges have a direction. Adjacency lists hold only the edges that leave a vertex.
Edge types of a depth-first forest, for an edge :
- Tree edge: the search reaches for the first time through this edge ( is white when the edge is examined).
- Back edge: is an ancestor of (or itself, a self-loop): is gray.
- Forward edge: is a descendant of but the edge is not a tree edge: is already black and was discovered after .
- Cross edge: every other edge: is black and was discovered before , in another branch or another tree.
Directed acyclic graph (DAG). A directed graph with no directed cycle.
Topological order. A list of all vertices in which every edge has before . It exists exactly for DAGs.
Strongly connected. Two vertices and are in the same strongly connected component (SCC) if there is a path from to and a path from to . The components partition the vertices; contracting each to a point gives the component graph, which is always a DAG.
How it works
Classifying edges
The animation runs the real code on a directed graph with vertices and edges. The kind of each edge is chosen by the same four lines that the page shows: white means tree, gray means back, black means forward if and cross otherwise.
Depth-first search on a directed graph: classifying every edge
Real runC source
color (W = white, G = gray, B = black)
discovery time disc[v]
finish time fin[v]
recursion stack (top on the right)
Adjacency lists (edges leave the vertex)
Edges classified so far
Blue = the position this step is working on, amber = a position being compared, red = positions just swapped, green = finished or in its final place, a filled blue cell = a value just placed, faded = not part of the structure at this step.
Step 1
Depth-first search on a directed graph: three strongly connected groups, {0, 1, 2}, {3, 4, 5} and {6, 7}.
The program prints every edge with its kind and then, further down, the times and the strongly connected components of this graph.
#include <stdio.h>
#define N 8
static int deg[N], nbr[N][N]; /* edges that leave each vertex, in increasing order */
static int tdeg[N], tnbr[N][N]; /* the reversed graph */
enum { WHITE, GRAY, BLACK };
static int color[N], disc[N], fin[N], timer, finish_order[N], nfin;
static void dfs_visit(int u, int print) {
color[u] = GRAY;
disc[u] = ++timer;
for (int k = 0; k < deg[u]; k++) {
int v = nbr[u][k];
const char *kind;
if (color[v] == WHITE) kind = "tree";
else if (color[v] == GRAY) kind = "back";
else if (disc[u] < disc[v]) kind = "forward";
else kind = "cross";
if (print) printf("edge %d -> %d %s\n", u, v, kind);
if (color[v] == WHITE) dfs_visit(v, print);
}
color[u] = BLACK;
fin[u] = ++timer;
finish_order[nfin++] = u;
}
static int comp[N];
static void collect(int u, int c) { /* a depth-first search in the reversed graph */
comp[u] = c;
for (int k = 0; k < tdeg[u]; k++) if (comp[tnbr[u][k]] < 0) collect(tnbr[u][k], c);
}
int main(void) {
int E[11][2] = {{0, 1}, {0, 3}, {1, 2}, {2, 0}, {2, 3}, {3, 4}, {4, 5}, {5, 3}, {6, 5}, {6, 7}, {7, 6}};
for (int i = 0; i < 11; i++) { nbr[E[i][0]][deg[E[i][0]]++] = E[i][1]; tnbr[E[i][1]][tdeg[E[i][1]]++] = E[i][0]; }
for (int v = 0; v < N; v++) if (color[v] == WHITE) dfs_visit(v, 1);
printf("times (disc/fin):");
for (int v = 0; v < N; v++) printf(" %d:%d/%d", v, disc[v], fin[v]);
printf("\nfinish order:");
for (int i = 0; i < nfin; i++) printf(" %d", finish_order[i]);
printf("\n");
for (int v = 0; v < N; v++) comp[v] = -1;
int count = 0;
for (int i = N - 1; i >= 0; i--) { /* decreasing finish time */
int r = finish_order[i];
if (comp[r] < 0) {
collect(r, count);
printf("component %d (root %d):", count, r);
for (int v = 0; v < N; v++) if (comp[v] == count) printf(" %d", v);
printf("\n");
count++;
}
}
printf("%d strongly connected components\n", count);
return 0;
}edge 0 -> 1 tree
edge 1 -> 2 tree
edge 2 -> 0 back
edge 2 -> 3 tree
edge 3 -> 4 tree
edge 4 -> 5 tree
edge 5 -> 3 back
edge 0 -> 3 forward
edge 6 -> 5 cross
edge 6 -> 7 tree
edge 7 -> 6 back
times (disc/fin): 0:1/12 1:2/11 2:3/10 3:4/9 4:5/8 5:6/7 6:13/16 7:14/15
finish order: 5 4 3 2 1 0 7 6
component 0 (root 6): 6 7
component 1 (root 0): 0 1 2
component 2 (root 3): 3 4 5
3 strongly connected componentsEvery edge of a directed graph classified by depth-first search: start
Computed by a script, not typed| edge | kind | times of the start u (disc/fin) | times of the end v (disc/fin) | why |
|---|---|---|---|---|
| 0 -> 1 | ||||
| 1 -> 2 | ||||
| 2 -> 0 | ||||
| 2 -> 3 | ||||
| 3 -> 4 | ||||
| 4 -> 5 | ||||
| 5 -> 3 | ||||
| 0 -> 3 | ||||
| 6 -> 5 | ||||
| 6 -> 7 | ||||
| 7 -> 6 |
Step 0
A depth-first search of a directed graph with 8 vertices and 11 edges. The kind of an edge u -> v depends on the colour of v when the edge is examined: white = tree, gray = back, black = forward (if v was discovered after u) or cross (if before).
The same facts in terms of the time stamps (all four kinds can be recognised afterwards from the intervals): for
| Kind | Intervals | Condition when examined |
|---|---|---|
| tree, forward | white (tree) or black with (forward) | |
| back | gray | |
| cross | black, |
Consequences.
- In a depth-first search of an undirected graph every edge is a tree edge or a back edge (forward edges are the same edges seen from the other end); there are no cross edges.
- A directed graph has a cycle if and only if the search finds a back edge. (A back edge closes the cycle along the tree path; conversely, a cycle has a first vertex discovered, and the vertex before it on the cycle becomes its descendant by the white-path theorem and gives a back edge.)
- In a DAG, for every edge we have .
Topological sort
By depth-first search. Run DFS, and write the vertices in decreasing finish time (put each vertex at the front of a list when it finishes). By fact 3 every edge goes from a vertex that finishes later to one that finishes earlier, so it points forward in the list. Time .
Kahn's algorithm. Repeatedly remove a vertex with in-degree and delete its outgoing edges (decreasing the in-degrees of its targets), appending the removed vertices in order. If the graph has a cycle the algorithm gets stuck with vertices left over (all remaining in-degrees positive): this is also a cycle test. Time .
A DAG usually has many topological orders. The program below computes the order from DFS, Kahn's order (always taking the smallest available vertex), and counts all valid orders by trying every permutation of the vertices.
#include <stdio.h>
#define N 8
static int deg[N], nbr[N][N], indeg[N];
enum { WHITE, GRAY, BLACK };
static int color[N], order[N], pos;
static void dfs_visit(int u) {
color[u] = GRAY;
for (int k = 0; k < deg[u]; k++) if (color[nbr[u][k]] == WHITE) dfs_visit(nbr[u][k]);
color[u] = BLACK;
order[--pos] = u; /* a vertex is put in front of everything finished before it */
}
static int valid(const int p[]) { /* every edge goes from an earlier to a later vertex */
int at[N];
for (int i = 0; i < N; i++) at[p[i]] = i;
for (int u = 0; u < N; u++) for (int k = 0; k < deg[u]; k++) if (at[u] > at[nbr[u][k]]) return 0;
return 1;
}
static long count_orders(int p[], int used, int k) { /* tries every permutation */
if (k == N) return valid(p);
long total = 0;
for (int v = 0; v < N; v++) if (!(used >> v & 1)) { p[k] = v; total += count_orders(p, used | 1 << v, k + 1); }
return total;
}
int main(void) {
int E[9][2] = {{0, 1}, {0, 2}, {1, 3}, {2, 3}, {2, 5}, {3, 4}, {5, 4}, {6, 2}, {6, 7}};
for (int i = 0; i < 9; i++) { nbr[E[i][0]][deg[E[i][0]]++] = E[i][1]; indeg[E[i][1]]++; }
pos = N;
for (int v = 0; v < N; v++) if (color[v] == WHITE) dfs_visit(v);
printf("DFS order (decreasing finish time):");
for (int i = 0; i < N; i++) printf(" %d", order[i]);
printf(" valid: %s\n", valid(order) ? "yes" : "no");
int kahn[N], done = 0, d[N];
for (int v = 0; v < N; v++) d[v] = indeg[v];
while (done < N) { /* Kahn: repeatedly remove the smallest vertex of in-degree 0 */
int u = -1;
for (int v = 0; v < N; v++) {
int taken = 0;
for (int i = 0; i < done; i++) if (kahn[i] == v) taken = 1;
if (!taken && d[v] == 0) { u = v; break; }
}
kahn[done++] = u;
for (int k = 0; k < deg[u]; k++) d[nbr[u][k]]--;
}
printf("Kahn order (smallest in-degree-0 vertex first):");
for (int i = 0; i < N; i++) printf(" %d", kahn[i]);
printf(" valid: %s\n", valid(kahn) ? "yes" : "no");
int p[N];
printf("number of different topological orders: %ld\n", count_orders(p, 0, 0));
return 0;
}DFS order (decreasing finish time): 6 7 0 2 5 1 3 4 valid: yes
Kahn order (smallest in-degree-0 vertex first): 0 1 6 2 3 5 4 7 valid: yes
number of different topological orders: 75Two topological orders of a DAG (there are 75 in all): start
Computed by a script, not typed| method | order | valid (checked) |
|---|---|---|
| DFS: decreasing finish time | ||
| Kahn: remove in-degree 0 vertices |
Step 0
The DAG has the edges 0->1, 0->2, 1->3, 2->3, 2->5, 3->4, 5->4, 6->2, 6->7. A topological order lists every vertex before the vertices it points to. A program tried all 8! permutations and counted 75 valid ones.
When is the order unique? Exactly when the DAG has a path through all vertices, that is, when consecutive vertices in the order are joined by edges (checked on 300 random DAGs). Otherwise there are at least two orders.
Strongly connected components (Kosaraju)
- Run DFS on and record the finish times.
- Build the reversed graph (all edges turned round). It has the same strongly connected components.
- Run DFS on , starting new trees from the vertices in decreasing finish time (of step 1). Each tree of this second search is one strongly connected component.
Why: in , the component that contains the vertex with the largest finish time is a source of the component graph (nothing enters it from another component); in it is a sink, so the search started there cannot leave it. Remove it and repeat. The components come out in a topological order of the component graph. Time . (Tarjan's algorithm finds the components in one search using low-link values.)
Strongly connected components by two depth-first searches (Kosaraju): start
Computed by a script, not typed| pass 2 start | root (largest unassigned finish time) | vertices of the component |
|---|---|---|
| component 0 | ||
| component 1 | ||
| component 2 |
Step 0
The first search (table above) gave the finish order 5 4 3 2 1 0 7 6. The second pass takes the vertices in decreasing finish time and searches in the reversed graph; each search finds one component.
The graph has three components: (a -cycle, with an edge leaving it), (a -cycle, with edges to ) and (a -cycle). The component graph and is a DAG, and the components were found in an order consistent with it (, then , then ).
Worked examples
EX 1Classify the edges by hand
EasyDirected graph , , , , . DFS from (neighbours in increasing order): (), edge white: tree; (), edge white: tree; (), edge : is gray: back edge (a cycle ); finishes (), finishes (); back at : edge white: tree; (), edge : is black and was discovered before (): cross edge; finishes (), finishes (). The graph is not a DAG (it has a back edge).
EX 2Topological order by finish times
EasyDAG with edges , , , . DFS from , neighbours in order: (1), (2), (3), finishes (4), finishes (5), (6): the edge leads to black ; finishes (7), finishes (8). Decreasing finish time: . Check: , , , all go forward. Another valid order is (Kahn); there are exactly .
EX 3Kahn's algorithm detects a cycle
MediumEdges , , , . In-degrees: : (from and ), : , : , : . Remove : the in-degree of drops to . Nothing has in-degree any more, but vertices are left: the algorithm stops early, so the graph has a cycle (here ).
EX 4How many strongly connected components?
MediumGraph on with edges , , , , , . The vertices reach each other; form a cycle; goes from the first group to the second but nothing comes back. Two components, and . A DAG with vertices has components (every vertex alone); a strongly connected graph has .
EX 5Typical statements
GATE level(a) A directed graph is acyclic iff DFS finds no back edge: true. (b) In a DFS of an undirected graph there can be a cross edge: false. (c) In a DAG, for every edge , : true. (d) A DAG has a unique topological order: false in general (unique only if there is an edge between each pair of consecutive vertices). (e) The time of topological sorting with adjacency lists: . (f) Which traversal order of vertices gives the topological order? Reverse of the order in which they finish (reverse postorder). (g) Maximum number of edges of a DAG on vertices: (a total order, one edge for every pair).
Cheat box
Cheat box
- Edge during DFS: white tree; gray back; black with forward; black with cross.
- Undirected: only tree and back edges. Directed: cycle back edge.
- DAG: for every edge . Topological order = decreasing finish time (or Kahn with in-degrees). . Unique iff a Hamiltonian path of consecutive edges.
- Kahn: stops early (with vertices left) iff a cycle exists.
- SCC (Kosaraju): DFS on ; reverse the edges; DFS in decreasing finish time; each tree is a component. . Components come out in topological order of the component graph; the component graph is a DAG.
- A DAG with vertices: at most edges, components.
Traps and GATE patterns
Connections
Builds on
Across subjects
Within this subject
All of this is DFS (BFS and DFS) with the colour rule. The reachability relation computed here for strong connectivity is the transitive closure that Floyd-Warshall computes for all pairs; the graphs are represented as in graph representations.
PYQs
10 previous year questions from your book. I re-solved each one myself (9 by running or simulating, 1 by reasoning only); the book's key is shown next to my answer. All PYQs
Let G be a weighted directed acyclic graph with m edges and n vertices. Given G and a source vertex s in G, which one of the following options gives the worst case time complexity of the fastest algorithm to find the lengths of shortest paths from s to all vertices that are reachable from s in G?
- (a)Θ(m + n)
- (b)Θ(m + n log(n))
- (c)Θ(nm)
- (d)Θ(n3)
Consider the following pseudocode for depth-first search (DFS) algorithm which takes a directed graph G(V,E) as input, where d[v] and f[v] are the discovery time and finishing time, respectively, of the vertex v ∈V. DFS(G) : unmark all v ∈ V t ← 0 for each v ∈ V if v is unmarked t ← Explore (G, v, t ) end if end for Explore ( G, v, t ): mark v t ← t + 1 d[v] ← t for each (v, w) ∈ E if w is unmarked t ← Explore (G, w, t) end if end for t ← t + 1 f[v] ← t return t Suppose that the input directed graph G(V,E) is a directed acyclic graph (DAG). For an edge (u,v)∈E, which of the following options will NEVER be correct?
- (a)d[u]<d[v]<f[v]<f[u]
- (b)d[v]<d[u]<f[u]<f[v]
- (c)d[v]<f[v]<d[u]<f[u]
- (d)d[u]<d[v]<f[u]<f[v]
Which of the following statements regarding Breadth First Search (BFS) and Depth First Search (DFS) on an undirected simple graph G is/are TRUE?
- (a)A DFS tree of G is a Shortest Path tree of G.
- (b)Every non-tree edge of G with respect to a DFS tree is a forward/back edge.
- (c)If (u, v) is a non-tree edge of G with respect to a BFS tree, then the distances from the source vertex s to u and v in the BFS tree are within ±1 of each other.
- (d)Both BFS and DFS can be used to find the connected components of G.
Let G be a directed graph and T a depth first search (DFS) spanning tree in G that is rooted at a vertex
Match Let G be a simple undirected graph. Let TD be a depth first search tree of G. Let TB be a breadth first search tree of G. Consider the following statements. (I) No edge of G is a cross edge with respect to TD. (A cross edge in G is between two nodes neither of which is an ancestor of the other in TD.) (II) For every edge (u,v) of G, if u is at depth i and v is at depth j in TB, then |i - j| = 1. Which of the statements above must necessarily be true?
- (a)I only
- (b)II only
- (c)Both I and II
- (d)Neither I nor II ALGORITHMS Check Out Our Gate Courses & Secure Your Dream Rank !
A depth-first search is performed on a directed acyclic graph. Let d[u] denote the time at which vertex u is visited for the first time and f[u] the time at which the DFS call to the vertex u terminates. Which of the following statements is always TRUE for all edges (u, v) in the graph ?
- (a)d[u] < d[v]
- (b)d[u] < f[v]
- (c)f[u] < f[v]
- (d)f[u] > f[v] ALGORITHMS Check Out Our Gate Courses & Secure Your Dream Rank !
Consider the DAG with V = {1,2,3,4,5,6}, shown
below
Which of the following is NOT a topological
ordering?- (a)1 2 3 4 5 6
- (b)1 3 2 4 5 6
- (c)1 3 2 4 6 5
- (d)3 2 4 1 6 5
Consider the depth-first-search of an undirected graph with 3 vertices P, Q, and R. Let discovery time d(u) represent the time instant when the vertex u is first visited, and finish time f(u) represent the time instant when the vertex u is last visited. Given that d(P)=5 units f(P)=12 units d(Q)=6 units f(Q)=10 units d(R)=14 unitf(R)=18 units Which one of the following statements is TRUE about the graph?
- (a)There is only one connected component
- (b)There are two connected components, and P and R are connected
- (c)There are two connected components, and Q and R are connected
- (d)There are two connected components, and P and Q are connected
Let T be a depth first search tree in a undirected graph G. Vertices u and v are leaves of this tree T. The degrees of both u and v in G are at least 2. Which one of the following statements is true?
- (a)There must exist a vertex w adjacent to both u and v in G
- (b)There must exist a vertex w whose removal disconnects u and v in G
- (c)There must be exist a cycle in G containing u and v
- (d)There must exist a cycle in G containing u and all its neighbours in G
An articulation point in a connected graph is a vertex such that removing the vertex and its incident edges disconnects the graph into two or more connected components. Let T be a DFS three
Self-check
Question 1
In a DFS of a directed graph, the edge reaches a gray vertex. What kind is it and what does it show?
Show answerHide
A back edge; the graph has a directed cycle.
Question 2
Give a topological order of the DAG with edges , , , .
Show answerHide
(the only one: before before before ).
Question 3
Why does Kosaraju's second search use the reversed graph?
Show answerHide
The vertex with the largest finish time lies in a source component of , which is a sink in : a search in from it stays inside its own component.
Question 4
How many strongly connected components does a DAG with vertices have?
Show answerHide
: every vertex is its own component.
Revision card
One-screen revision
- Edge kinds by colour of : white tree, gray back, black forward () or cross.
- Directed cycle back edge. Undirected: no cross edges.
- Topological order: decreasing finish time or Kahn; DAGs only; ; unique iff consecutive edges.
- SCC: DFS, reverse graph, DFS in decreasing finish; component graph is a DAG.
References
- ·Cormen, Leiserson, Rivest, Stein, Introduction to Algorithms (3rd edition), Chapter 22 (22.3 properties of depth-first search, 22.4 topological sort, 22.5 strongly connected components)
- ·Sedgewick and Wayne, Algorithms (4th edition), Section 4.2 Directed Graphs
- ·Dasgupta, Papadimitriou, Vazirani, Algorithms, Sections 3.2 to 3.4 (depth-first search in directed graphs, strongly connected components)
Verification
On 1000 random graphs (500 undirected, 500 directed) the edge kinds decided by the colour rule were compared with the kinds decided from the discovery and finish intervals; undirected graphs had no cross edges; a directed graph had a back edge exactly when a reachability test found a cycle. Topological orders: reversed finish order and Kahn's order were valid on 400 random DAGs and found among all valid permutations, every DAG search had no back edge, the number 75 of orders of the page DAG was found by trying all 8! permutations, and the claim that the order is unique exactly when consecutive vertices are joined by edges was checked on 300 random DAGs. Kosaraju's components were compared with mutual reachability on 500 random digraphs and the components were found in topological order of the component graph. The animation and the three tables are real runs of the C code on the page. Textbook chapter references are from memory. SECOND PASS (2026-11-02, done by the assistant, not a human check): every Self-check question was solved before the printed answer was read and all agreed; the definitions, worked examples, cheat box and traps were read claim by claim and the numbers re-derived by hand or by a small script (loop counts, recurrences, merge and quicksort counts and averages, hash probes and load-factor formulas, DP tables, MST and shortest-path runs, Floyd matrices); long C programs were not re-read line by line (they are compiled and compared by npm run verify:c, and their printed outputs were checked against the prose); tables and animations are machine output and were read, not re-run; No other defect found. Status raised to self-checked; verified needs a textbook or human check.
last verified: never
My notes (saved in this browser)