Topological Sort Dependency Ordering
Order vertices of a directed acyclic graph so that every edge points forward β prerequisites before dependents, courses before advanced courses, tasks before the tasks that need them. Two algorithms: Kahn's BFS and DFS post-order.
What is Topological Sort?
A topological sort produces a linear ordering of vertices in a directed acyclic graph (DAG) such that for every directed edge uβv, vertex u appears before vertex v in the ordering. It answers the question: "in what order should I process these tasks if some tasks depend on others?" The ordering is NOT unique β a DAG may have multiple valid topological orders. Topological sort only works on DAGs; if the graph has a cycle, no valid ordering exists (you can't put A before B and B before A simultaneously). There are two standard algorithms: Kahn's algorithm (BFS-based, using in-degrees) and DFS post-order (reverse the finish order). Both run in O(V + E). Topological sort appears in interviews as "Course Schedule" (LC 207/210), build dependency resolution, compilation order, and any problem where you need to process nodes in dependency order.
How It Thinks
Kahn's Algorithm (BFS + In-Degree)
Kahn's algorithm works by repeatedly removing vertices with no incoming edges (in-degree 0). When you remove a vertex, decrement the in-degree of all its neighbours. If a neighbour's in-degree drops to 0, it's now "ready" β add it to the queue. Continue until the queue is empty. If all vertices were processed, the order is valid; otherwise, a cycle exists.
- It's iterative (no recursion stack overflow), gives the topological order directly, and detects cycles as a bonus (
order.size() < V). - For "Course Schedule II" (LC 210), it's the cleanest solution.
DFS-Based Topological Sort
Run DFS from every unvisited vertex. When a vertex finishes (all descendants explored), push it onto a stack (or prepend to a list). The final stack order (top to bottom) is a valid topological order. Intuitively: a vertex finishes AFTER all its dependents finish β so reversing the finish order puts prerequisites first.
- In DFS, a vertex finishes after ALL its descendants have finished. If uβv, then v finishes before u.
- So in the post-order list, v appears before u β reversing gives u before v, which is topological order.
Detecting Cycles in DAGs
A topological sort only exists for DAGs. If the graph has a cycle, we need to detect it and return "impossible." Both algorithms detect cycles naturally:
Count processed vertices
If order.size() < V after the queue empties, some vertices never reached in-degree 0 β they're part of a cycle. This is the cleanest cycle detection for directed graphs.
Detect back edges
Mark vertices: WHITE (unvisited), GREY (in progress), BLACK (done). If DFS reaches a GREY vertex β back edge β cycle. This is the standard directed cycle detection.
- "Can you finish all courses?" = "Is the prerequisite graph a DAG?" Run Kahn's and check
order.size() == numCourses. - If yes β return true. If no β cycle β return false.
Kahn's vs DFS β Which to Use
| Aspect | Kahn's (BFS) | DFS Post-Order |
|---|---|---|
| Approach | Iterative (queue) | Recursive (stack) |
| Cycle detection | order.size() < V | GREYβGREY back edge |
| Output | Direct topological order | Reverse of post-order |
| Stack overflow risk | None (iterative) | Yes, on deep graphs |
| Lexicographic order | Easy: use PriorityQueue | Harder to guarantee |
| Interview preference | β More common, cleaner | Option if DFS is already built |
| Time / Space | O(V+E) / O(V) | O(V+E) / O(V) |
- It's iterative, gives the answer directly, and cycle detection is a one-line check.
- Use DFS only when the problem already requires DFS traversal (e.g., "Alien Dictionary" where you build the graph AND topo-sort it).
When to Reach for Topological Sort
| Signal | Pattern | Example |
|---|---|---|
| "prerequisites" / "dependencies" / "ordering" | Topo sort (Kahn's) | Course Schedule I & II (LC 207, 210) |
| "can all tasks be completed?" | Cycle detection via topo sort | Course Schedule I (LC 207) |
| "return an ordering that satisfies all constraints" | Kahn's or DFS topo sort | Course Schedule II (LC 210) |
| "alien dictionary" / "infer order from comparisons" | Build DAG from pairs β topo sort | Alien Dictionary (LC 269) |
| "parallel task scheduling" / "minimum time" | Topo sort + level tracking (longest path in DAG) | Parallel Courses (LC 1136) |
| "compilation order" / "build order" | Topo sort on dependency graph | Build system dependencies |
Build It Once
Build Kahn's once β it's the interview standard. The structure is always the same: build adjacency list, compute in-degrees, BFS from in-degree-0 vertices, check cycle.
Use It In Java
List<List<Integer>> adj = new ArrayList<>(); β initialise n empty lists int[] inDeg = new int[n]; β increment for each incoming edge Deque<Integer> q = new ArrayDeque<>(); β Kahn's BFS queue | Kahn's Step | Java Code | Note |
|---|---|---|
| Build graph | adj.get(u).add(v); inDeg[v]++; | Read edge direction carefully (prereqβcourse) |
| Seed queue | if (inDeg[i] == 0) q.offer(i); | All vertices with no incoming edges |
| Process | int u = q.poll(); order[idx++] = u; | Take one ready vertex |
| Decrement | if (--inDeg[v] == 0) q.offer(v); | Pre-decrement + check in one line |
| Cycle check | return idx == n ? order : new int[0]; | Empty array = cycle detected |
[course, prereq] β the edge goes FROM prereq TO course (adj.get(prereq).add(course)). Reversing this produces a wrong answer. Always clarify: "X is a prerequisite of Y" means edge XβY.
ArrayDeque with PriorityQueue in Kahn's. The min-heap always picks the smallest ready vertex first. This changes nothing about correctness but guarantees a unique canonical ordering.
Problems To Solve
Topological sort appears in interviews as "Course Schedule" or a disguised dependency-ordering problem. The key skill is recognising "prerequisites = directed edges β topo sort." Once recognised, Kahn's template is mechanical. The hard problems (Alien Dictionary, Parallel Courses) add a graph-construction step before the topo sort itself.
| Difficulty | Pattern | Problem | Key Insight |
|---|---|---|---|
| Medium | cycle | Course Schedule β LC 207 | "Can you finish all courses?" = "Is the graph a DAG?" Run Kahn's, check order.size() == numCourses. |
| Medium | topo | Course Schedule II β LC 210 | Return a valid ordering. Kahn's directly produces the answer. Return empty array if cycle. |
| Hard | topo+build | Alien Dictionary β LC 269 | Build a character DAG from adjacent word pairs (first differing character = edge). Then Kahn's on the character graph. Edge case: "abc" before "ab" is invalid. |
| Medium | topo+dp | Parallel Courses β LC 1136 | Topo sort with level tracking. Process layer by layer (like BFS levels). Number of layers = minimum semesters. If cycle β return -1. |
| Medium | topo | Minimum Height Trees β LC 310 | Not a classic topo sort but uses the in-degree peeling technique: repeatedly remove leaves (degree 1) until 1β2 nodes remain. Those are the roots of minimum-height trees. |
| Medium | topo | Longest Increasing Path in a Matrix β LC 329 | Each cell is a node, edge from smaller to larger neighbour. Topo sort on this implicit DAG. Longest path = max level in topo order. Also solvable with DFS + memoisation. |
- When you see "prerequisites," "dependencies," "ordering constraints," or "can all tasks be completed" β think topological sort.
- Build the graph, run Kahn's, check for cycles.
- The template is always: in-degree array β seed queue β BFS process β check
count == V.