Graph Algorithm
Kahn's Algorithm
“The Dependency Resolver”
History
If you have ever been stuck in "Dependency Hell" while trying to install a node module, you have Arthur Kahn (1962) to thank for the solution.
This is the standard algorithm for Topological Sorting. It takes a graph of tasks where some tasks must finish before others can start (a Directed Acyclic Graph, or DAG) and flattens it into a linear to-do list.
How It Works
Kahn's algorithm simulates a production line. It keeps track of "Indegrees" -- the number of prerequisites a task has left.
The Algorithm Step-by-Step
- Calculate Indegrees: Count how many incoming edges each node has.
- Queue Zeroes: Find all nodes with 0 incoming edges (tasks with no prerequisites). Put them in a Queue.
- Process:
- Dequeue a node and add it to the final sorted list.
- "Delete" the node from the graph (virtually).
- Decrement the indegree of all its neighbors.
- If a neighbor's indegree hits 0, add it to the Queue.
- Repeat: Until the queue is empty.
Cycle Detection
If the queue empties but you haven't processed all nodes, it means there is a cycle (a deadlock). For example, A waits for B, and B waits for A.
We visit every node and every edge exactly once. It is extremely efficient.
We need an array to store indegrees and a queue for the zero-degree nodes.
The core data structure powering Kahn's Algorithm.
How this algorithm appears during visualization.
Code Walkthrough
Real implementations in multiple programming languages. Select a language to view idiomatic code.
1from collections import deque23def kahn(n: int, edges: list[tuple[int, int]]) -> list[int] | None:4 """Topological sort using Kahn's algorithm (BFS-based).56 Args:7 n: Number of vertices (0 to n-1)8 edges: List of (u, v) tuples meaning u -> v (u must come before v)910 Returns:11 List of vertices in topological order, or None if cycle detected12 """13 # Build adjacency list and compute indegrees14 graph: list[list[int]] = [[] for _ in range(n)]15 indegree: list[int] = [0] * n1617 for u, v in edges:18 graph[u].append(v)19 indegree[v] += 12021 # Initialize queue with all nodes having indegree 022 # (nodes with no prerequisites)23 queue: deque[int] = deque()24 for node in range(n):25 if indegree[node] == 0:26 queue.append(node)2728 result: list[int] = []2930 while queue:31 # Process node with no remaining dependencies32 node = queue.popleft()33 result.append(node)3435 # "Remove" this node by decrementing neighbor indegrees36 for neighbor in graph[node]:37 indegree[neighbor] -= 13839 # If neighbor now has no dependencies, add to queue40 if indegree[neighbor] == 0:41 queue.append(neighbor)4243 # If we processed all nodes, we have a valid ordering44 # Otherwise, there's a cycle45 if len(result) == n:46 return result47 else:48 return None # Cycle detectedReal World Use Cases
- 1Build Systems (Make, Webpack, determining compilation order)
- 2Package Managers (resolving npm/pip dependencies)
- 3Task Scheduling (Project management software)
- 4Excel Formula Evaluation (calculating cell dependencies)
- 5University Course Prerequisites
Key Insights
- It ONLY works on DAGs (Directed Acyclic Graphs).
- It provides built-in Cycle Detection.
- The result is not unique (multiple valid sort orders can exist).
- It is a BFS-based approach to topological sort.
When to Use
Whenever you have a set of tasks with dependencies and need to find a valid order to execute them. Also useful for detecting circular dependencies.
When NOT to Use
Do not use on undirected graphs. Do not use if the graph contains cycles (unless your goal is specifically to detect them).
Interview Tip
This is the go-to algorithm for the 'Course Schedule' problem on LeetCode. Remember: if the output length != number of nodes, you have a cycle.