22. Topological Sort & DAG Longest Path
822 words
4 min read
Visual companion
Python
Type and operator map
Python Week 1: the first filter for runtime behavior
View
Revision summary
What this note is really saying
Short form
# 22. Topological Sort & DAG Longest Path > **What problem does this solve?** Some tasks must be done in order: you must put on socks before shoes, finish course prerequisites before taking advanced courses.

22. Topological Sort & DAG Longest Path
What problem does this solve? Some tasks must be done in order: you must put on socks before shoes, finish course prerequisites before taking advanced courses. A topological sort gives a linear ordering of vertices in a Directed Acyclic Graph (DAG) such that for every edge u→v, u comes before v.
1. Kahn's Algorithm (BFS-based)
How It Works
- Compute in-degree (incoming edges) for each vertex
- Start with vertices having in-degree 0
- Remove a vertex, decrease in-degree of its neighbors
- If a neighbor's in-degree becomes 0, add it to the queue (Diagram)
Implementation
python# runnable from collections import deque def topological_sort_kahn(graph): """Return topological ordering using Kahn's algorithm. Time: O(V + E) Space: O(V) """ # Compute in-degrees in_degree = {v: 0 for v in graph} for v in graph: for neighbor in graph[v]: in_degree[neighbor] += 1 # Queue of vertices with in-degree 0 queue = deque([v for v in graph if in_degree[v] == 0]) result = [] while queue: v = queue.popleft() result.append(v) for neighbor in graph[v]: in_degree[neighbor] -= 1 if in_degree[neighbor] == 0: queue.append(neighbor) # Check if graph had a cycle if len(result) != len(graph): return None # Cycle detected! return result # DAG for getting dressed dress_graph = { 'undershorts': ['pants', 'shirt'], 'pants': ['belt', 'shoes'], 'belt': ['jacket'], 'shirt': ['belt', 'jacket'], 'tie': ['jacket'], 'jacket': [], 'socks': ['shoes'], 'shoes': [], 'watch': [] } result = topological_sort_kahn(dress_graph) print(f"Topological order: {result}") # One valid order: ['socks', 'undershorts', 'pants', 'shoes', 'watch', # 'shirt', 'belt', 'tie', 'jacket']
2. DFS-based Topological Sort
How It Works
- Run DFS, recording postorder numbers
- Sort vertices by decreasing postorder number (reverse of finishing order)
python# runnable def topological_sort_dfs(graph): """Return topological ordering using DFS (reverse postorder).""" visited = set() result = [] def dfs(v): visited.add(v) for neighbor in graph[v]: if neighbor not in visited: dfs(neighbor) result.append(v) # Postorder: add after processing children for v in graph: if v not in visited: dfs(v) # Reverse to get topological order return list(reversed(result)) print(f"DFS-based: {topological_sort_dfs(dress_graph)}")
3. Cycle Detection
If Kahn's algorithm processes fewer than |V| vertices, the graph has a cycle.
python# runnable def has_cycle(graph): """Detect cycle in directed graph using DFS.""" WHITE, GRAY, BLACK = 0, 1, 2 color = {v: WHITE for v in graph} def dfs(v): color[v] = GRAY # In current recursion stack for neighbor in graph[v]: if color[neighbor] == GRAY: return True # Back edge → cycle! if color[neighbor] == WHITE: if dfs(neighbor): return True color[v] = BLACK # Fully processed return False for v in graph: if color[v] == WHITE: if dfs(v): return True return False # Test cyclic_graph = { 'A': ['B'], 'B': ['C'], 'C': ['A'] # Back edge! } print(f"Cyclic: {has_cycle(cyclic_graph)}") # True print(f"DAG: {has_cycle(dress_graph)}") # False
4. Longest Path in DAG
In a DAG, the longest path can be found in O(V + E) using DP on topological order.
python# runnable def longest_path_dag(graph, weights, start): """Find longest path from start in a weighted DAG. Uses DP: dist[v] = max(dist[v], dist[u] + weight(u,v)) Processed in topological order. """ topo_order = topological_sort_kahn(graph) if topo_order is None: return None # Has cycle dist = {v: float('-inf') for v in graph} dist[start] = 0 for u in topo_order: if dist[u] != float('-inf'): for v, w in graph[u]: if dist[u] + w > dist[v]: dist[v] = dist[u] + w return dist # Longest path example dag = { 'A': [('B', 3), ('C', 2)], 'B': [('C', 1), ('D', 5)], 'C': [('D', 4)], 'D': [] } print(f"Longest paths from A: {longest_path_dag(dag, 'A')}") # {'A': 0, 'B': 3, 'C': 3, 'D': 8} (A→B→D = 3+5=8)
Practice Questions
Q1. Find a topological order for: A→B, A→C, B→D, C→D.
Q2. Can you topological-sort a graph with a cycle?
Q3. Why does Kahn's algorithm use a queue instead of a stack?
Q4. How does pre/post numbering in DFS help detect cycles?
Q5. For a DAG with n vertices, what's the minimum and maximum number of edges?
AnswersA1. A before B and C, both before D. Valid orders: [A, B, C, D] or [A, C, B, D].A2. No — topological sort is only defined for DAGs. A cycle means no linear ordering exists.A3. Queue gives BFS-like behavior (any vertex with in-degree 0 works). Using a stack would also produce a valid topological sort, but the order would differ (DFS flavor).A4. If during DFS we encounter a GRAY vertex (currently on the recursion stack), we've found a back edge → cycle. Finished/BLACK vertices are safe.A5. Minimum: n - 1 (a single path). Maximum: n(n-1)/2 (complete DAG — all edges go from lower-index to higher-index vertices). Join Discord Previous21. BFS & DFS — Graph TraversalsNext23. Dijkstra's Shortest Path Algorithm