Home
Day 11 · Week 3

Kahn's Algorithm (BFS Topological Sort)

Level 4 · Topological Sort  ·  2 hrs: ~45 min theory · ~60 min code · ~15 min notes

🧠 Mental Model

A topological order is a linear arrangement of a DAG's vertices such that every edge u → v points "forward" (u before v). Kahn's builds it by repeatedly peeling off vertices with no remaining prerequisites (in-degree 0). It's BFS with in-degree bookkeeping.

Think dependency resolution: a task can run only once all its prerequisites are done. Kahn's runs the ready tasks, which "unlocks" their dependents, and so on. If tasks remain but nothing is ready, you have a circular dependency — a cycle.

⚙️ Mechanics

  1. Compute indegree[v] for all vertices.
  2. Enqueue every vertex with indegree = 0.
  3. Pop u, append to output; for each edge u → v, decrement indegree[v]; if it hits 0, enqueue v.
  4. If the output has fewer than V vertices, the graph has a cycle (no valid order).
Invariant: a vertex is enqueued only after all its predecessors have already been output. So the output order respects every edge.

📐 Correctness

Theorem Kahn's algorithm outputs a valid topological order iff the graph is a DAG; otherwise it outputs fewer than V vertices.
Proof (Validity) When v is enqueued, indegree[v] has been decremented to 0, meaning every predecessor u was already popped and output before v. Thus for every edge u → v, u precedes v — the defining property.

(Cycle ⇒ incomplete) Every vertex on a directed cycle has an in-edge from another cycle vertex, so its in-degree never reaches 0 (each decrement requires a predecessor to be output first, but they mutually block). Those vertices are never enqueued, so |output| < V. Conversely, a DAG always has a source (in-degree 0) — else follow in-edges backward forever in a finite graph, forcing a cycle — so the queue never empties early.

📊 Complexity

MetricValueWhy
TimeO(V + E)Each vertex enqueued once; each edge relaxes one in-degree.
SpaceO(V)In-degree array + queue.
Kahn vs DFS topo (Day 12): Kahn's gives cycle detection "for free" via the count check and naturally yields lexicographically-smallest orders if you use a min-heap. DFS-based is terser and pairs with other DFS work.

💻 Reference Implementation (Java)

import java.util.*;

/** Kahn's topo sort. Returns order, or empty list if a cycle exists. */
public List<Integer> topoSort(int n, List<Integer>[] adj) {
    int[] indeg = new int[n];
    for (int u = 0; u < n; u++)
        for (int v : adj[u]) indeg[v]++;

    Queue<Integer> q = new ArrayDeque<>();
    for (int v = 0; v < n; v++) if (indeg[v] == 0) q.offer(v);

    List<Integer> order = new ArrayList<>();
    while (!q.isEmpty()) {
        int u = q.poll();
        order.add(u);
        for (int v : adj[u])
            if (--indeg[v] == 0) q.offer(v);   // v now unlocked
    }
    // fewer than n ⇒ cycle ⇒ no valid order
    return order.size() == n ? order : new ArrayList<>();
}
// For lexicographically smallest order: use a PriorityQueue instead of ArrayDeque.

🎭 Problem Variances — how Big Tech disguises topological sort

Interviewers rarely say "topologically sort this." You detect it by the dependency / ordering-with-prerequisites shape. Common disguises:

Disguise / phrasingWhat's really askedTwist to handle
"Course schedule / prerequisites"Is a valid order possible? (cycle check)Return boolean vs the actual order
"Build/compile order," "task scheduling"Produce one topo orderTies → any order, or lexicographically smallest (min-heap)
"Alien dictionary / recover order"Infer edges from adjacent items, then topo sortEdge extraction is the hard part; watch invalid-prefix edge cases
"Unique reconstruction?" (seq. reconstruction)Topo order that is uniqueQueue must never hold >1 element at a time
"Parallel courses / min semesters"Longest path in the DAG = # of Kahn layersProcess level-by-level (like BFS rings), count layers
"Minimum height trees"Peel leaves inward (undirected topo-style)Peel in-degree-1 nodes; stop at ≤2 centers
"Detect deadlock / eventual safe states"Cycle membershipReverse-graph Kahn, or DFS coloring (Day 12)
Recognition heuristic: if items have "must come before" relationships and the graph is (or should be) acyclic, it's topological sort. If it must be unique, add the "queue size ≤ 1" check.

⚠️ Common Pitfalls

Forgetting the cycle check: if you don't compare order.size() to n, a cyclic graph silently returns a partial order.
Building the graph backwards: "a depends on b" means edge b → a. Getting direction wrong reverses everything.
Decrementing in-degree at the wrong time: only enqueue when it reaches exactly 0, not on every decrement.

🎯 Problems

LC 207 Course Schedule Cycle-check variant — return whether a valid order exists. Med
LC 210 Course Schedule II Return the order itself; empty array if impossible. Med
LC 1136 Parallel Courses Layered Kahn — count BFS layers = minimum semesters (longest path). Med

📚 References