Applications: Shortest Paths, Components, Topological Sort
Three classic problems solved by BFS or DFS in linear time: shortest paths in an unweighted graph (BFS), counting connected components (BFS or DFS), and topological sort of a DAG (DFS finish-time order or Kahn's BFS-style algorithm).
Three problems, two algorithms
Each of these three classic problems reduces to a single BFS or DFS pass. None requires more than the linear-time traversals you have already seen — but the trick is in how you read off the answer from the traversal.
- Shortest path (unweighted graph) — BFS, since each layer = one extra edge.
- Connected components — BFS or DFS, run from every unvisited vertex; the number of restarts is the component count.
- Topological sort (on a DAG) — DFS, ordered by decreasing finish time; equivalent to Kahn's algorithm, a BFS over vertices with zero in-degree.
1. Shortest paths in an unweighted graph
On an unweighted graph, every edge counts as one step. BFS explores in layers — layer k = all vertices at distance exactly k — so the first time it reaches a vertex is via a shortest path.
Algorithm: run BFS from the source s, recording dist[v] = dist[parent] + 1. To recover the path itself, also store parent[v] and walk it backward from the destination.
Step-by-step trace on the same graph as the BFS chapter, BFS from 1:
dist[6] = 3, and parent gives the shortest path 1 → 2 → 4 → 6.
For weighted graphs: BFS no longer works because layer order no longer corresponds to total weight. Use Dijkstra (non-negative weights) or Bellman–Ford (general weights).
2. Counting connected components
A connected component of an undirected graph is a maximal set of vertices reachable from each other. The algorithm is the simplest application of BFS/DFS:
Each restart kicks off exploration of a brand-new component, so the number of restarts is the number of components. Often we also record comp[v] — the component ID — so we can answer 'are u and v connected?' in O(1) afterwards.
Step-by-step on a disconnected graph:
For directed graphs the analogous concept is strongly connected components (SCC), which requires Tarjan's or Kosaraju's algorithm — beyond this section, but built on the same DFS skeleton.
3. Topological sort of a DAG
A topological sort of a directed acyclic graph (DAG) is a linear ordering of its vertices such that for every edge u → v, vertex u appears before v in the ordering. Useful any time something must be done in dependency order — course prerequisites, build systems, task scheduling.
There are two standard approaches, both linear time:
(a) DFS-based. Run DFS. When a vertex finishes (its recursive call returns), push it onto a stack. After DFS is done, popping the stack gives a valid topological order.
Why it works: if
u → vexists, the recursion aturecurses intov(orvwas finished earlier), sofin[v] < fin[u]—ufinishes afterv. Sorting by decreasing finish time putsubeforev.
(b) Kahn's algorithm (BFS-based). Maintain inDeg[v]. Initialize a queue with every vertex of in-degree 0. Repeatedly pop a vertex u, append it to the output, and decrement inDeg[v] for each neighbor v; whenever inDeg[v] hits 0, enqueue v. If you eventually output fewer than V vertices, the graph has a cycle.
Step-by-step with Kahn's algorithm on a course-dependency DAG:
All 6 vertices were emitted, so the graph is a DAG and the output is a valid topological order:
Kahn's algorithm doubles as a cycle detector: if |output| < V at the end, some vertices never reached in-degree 0, which means they sit on a cycle.
Pseudocode
All three solutions side by side: BFS shortest paths with parent tracking, component counting by restarting the traversal, and both topological-sort variants — DFS finish times and Kahn's in-degree queue. Each is a self-contained function in the C++ tab; pick whichever fits the problem you're solving.
1// 1. Unweighted shortest paths from s2BFS-SHORTEST-PATHS(G, s):3 for each v in G: dist[v] = ∞, parent[v] = NIL4 dist[s] = 0; enqueue(Q, s)5 while Q not empty:6 u = dequeue(Q)7 for each neighbor v of u:8 if dist[v] == ∞:9 dist[v] = dist[u] + 110 parent[v] = u11 enqueue(Q, v)12 return dist, parent1314// 2. Count connected components (undirected graph)15COUNT-COMPONENTS(G):16 visited[v] = false for all v17 count = 018 for each v in G:19 if not visited[v]:20 count = count + 121 BFS or DFS from v — mark every reached vertex visited22 return count2324// 3a. Topological sort via DFS finish times25TOPO-SORT-DFS(G):26 order = empty stack27 visited[v] = false for all v28 for each v in G:29 if not visited[v]: DFS-VISIT(v, order)30 return order popped one at a time (top first)3132DFS-VISIT(u, order):33 visited[u] = true34 for each neighbor v of u:35 if not visited[v]: DFS-VISIT(v, order)36 push u onto order // u finishes — append to stack3738// 3b. Topological sort via Kahn (BFS on in-degrees)39TOPO-SORT-KAHN(G):40 compute inDeg[v] for all v41 Q = queue of all v with inDeg[v] = 042 order = []43 while Q not empty:44 u = dequeue(Q)45 append u to order46 for each neighbor v of u:47 inDeg[v] -= 148 if inDeg[v] == 0: enqueue(Q, v)49 if |order| < V: report "cycle"50 return order
Complexity summary
All three applications inherit the cost of one BFS or DFS pass plus O(V) bookkeeping.
| Problem | Algorithm | Time | Space |
|---|---|---|---|
Unweighted shortest paths from s | BFS | O(V + E) | O(V) |
| Connected components | BFS or DFS, restarted on each unvisited vertex | O(V + E) | O(V) |
| Topological sort | DFS (finish times) or Kahn (BFS on in-degrees) | O(V + E) | O(V) |
No trick — every vertex is processed once and every edge inspected a constant number of times.
Complexity Analysis
Time Complexity
All three applications run in O(V + E) on a graph stored as an adjacency list.
Space Complexity
O(V) extra for the queue/stack and the dist / visited / inDeg arrays.
BFS-based shortest paths only work in unweighted graphs (or uniform-weight ones). Topological sort exists iff the graph is a DAG — both Kahn's and DFS-based versions naturally detect the presence of a cycle.