Topological Sort
Put tasks in an order where every task comes after the tasks it depends on.
01 · why it exists
Why this exists, and the weight it carries
since1962
Topological sort with Kahn's algorithm
A. B. Kahn (Westinghouse Electric Corporation, Baltimore)
In the late 1950s, big projects such as the US Navy's Polaris missile programme were planned as PERT networks: thousands of events, each waiting for others. Before a computer could analyse such a network, the events had to be listed so that every event came after the ones it waits for. In 1962 A. B. Kahn published "Topological sorting of large networks" in Communications of the ACM. He estimated that, on an IBM 7090, a 30,000-activity PERT network could be ordered in under an hour of machine time — though, as he noted, the method had not yet been programmed.
the problem it solvedPutting thousands of dependent events in a valid order — and noticing when the dependencies loop.
its jobs today
- Build tools: Gradle builds a directed acyclic graph of tasks before running any of them; Maven's reactor sorts modules so each is built before it is needed.
- Spreadsheets: Excel turns formula dependencies into a calculation chain — the order cells must be calculated in — and warns about circular references.
- Workflow schedulers: an Apache Airflow DAG decides which task may start once its upstream tasks are done.
the weight it carries
Illustrative: a build with 2,000 modules and 10,000 dependency edges. Kahn: 2,000 + 10,000 = 12,000 steps to get a build order or report a cycle. Rescanning for a free module every round instead: up to 2,000 rounds × 12,000 = 2.4·10⁷ steps.
without it
Builds, spreadsheets and schedulers would have to guess an order and retry, and a circular dependency would hang instead of failing with a clear error.
when · why · how
- when
- Prerequisites, dependencies, a build order — or "is it possible at all?"
- why
- A vertex with in-degree 0 has nothing left to wait for, so it can go next. Each vertex is polled once and each edge decremented once: O(V + E), no recursion, and a free cycle check (output count < V).
- how
int[] indegree,List<List<Integer>> adjand anArrayDeque<Integer>queue; swap in aPriorityQueue<Integer>for the smallest order.
since1976
Topological order by DFS (reverse post-order)
Robert E. Tarjan
The second classic method comes from depth-first search: a vertex finishes only after everything it points to has finished, so the reversed finish order is a topological order. Its first description in print is usually traced to Tarjan's 1976 paper "Edge-disjoint spanning trees and depth-first search" — part of his work showing how much a single DFS reveals about a graph. It is the version taught in Cormen et al.'s Introduction to Algorithms.
the problem it solvedOrdering a dependency graph — and spotting which vertices sit on or lead into a cycle — with the DFS you already know.
its jobs today
- Per-vertex answers in the same pass: which nodes are safe (LC 802), which modules sit on a dependency cycle.
- Strongly connected components (Kosaraju, Tarjan) are built on DFS finish order — the same idea one level up.
- Memoised DFS on a DAG (longest increasing path, counting paths) computes values in reverse topological order without ever sorting.
the weight it carries
Illustrative: 1,500 packages and 6,000 "depends on" edges. One DFS: 1,500 + 6,000 = 7,500 steps for a full order. The call stack only grows as deep as the longest chain: 20 frames for a 20-package chain — but 10⁵ frames on a 10⁵-long chain, where Java's stack can overflow.
without it
Every DFS-based routine — safe states, components, memoised DAG DP — would need a separate Kahn pass just to get an order.
when · why · how
- when
- You already have a DFS skeleton, or you need per-vertex results (safe / unsafe, memo values) along with the order.
- why
- When u finishes, everything it points to has finished, so reversing the finish order puts u before them. O(V + E) — but the recursion can go V deep.
- how
int[] color(0 white, 1 gray, 2 black);pushonto anArrayDeque<Integer>when a vertex turns black; an edge into gray means a cycle. For very deep inputs, use Kahn.
since1957–1959
Critical path (DP on a DAG)
James E. Kelley Jr. & Morgan R. Walker (CPM); Malcolm, Roseboom, Clark & Fazar (PERT)
In 1957 duPont and Remington Rand developed the Critical-Path Method and piloted it on duPont's UNIVAC I; Kelley and Walker published it in 1959. At the same time the US Navy's Polaris programme planned its research work as PERT networks of events in dependency order (published 1959). Both ask one question: with tasks running in parallel, which chain of dependent tasks decides the finish date? Kelley and Walker reported that one plant-shutdown test paid for the method's whole development five times over.
the problem it solvedThe earliest finish date of a project whose tasks depend on each other — and which tasks must not slip.
its jobs today
- Project planning tools compute each task's earliest start and finish and highlight the critical path.
- Build and CI pipelines: with enough parallel workers, the total time is the longest chain of dependent steps.
- Any DP on a DAG — longest path, earliest finish, number of ways — runs in topological order in O(V + E).
the weight it carries
Illustrative: 5·10⁴ tasks and 5·10⁴ dependencies (LC 2050's limits). One Kahn pass + DP: about 10⁵ steps for every task's earliest finish. Listing paths instead explodes: a chain of 30 "diamonds" alone has 2³⁰ ≈ 1.07·10⁹ paths.
without it
Longest path is NP-hard on general graphs. Without a DAG and its topological order, "when does the project finish?" would mean exploring every path.
when · why · how
- when
- Dependencies plus durations: earliest finish, minimum time with unlimited parallelism, the longest chain.
- why
- In topological order every predecessor is final before you read it, so
finish[v] = max(finish[v], finish[u] + time[v])is right in one pass: O(V + E). - how
- Kahn's loop with an
int[](orlong[]) finish array; update each edge when its source is polled; the answer is the largest finish.
since1869
Tree centre by peeling leaves
Camille Jordan
In 1869 the French mathematician Camille Jordan studied trees in "Sur les assemblages de lignes". Graph theory books credit him with the fact behind Minimum Height Trees: every tree's centre is either one vertex or two adjacent vertices. Peeling the leaves layer by layer finds it — Kahn's idea applied to an undirected tree.
the problem it solvedWhere to root a tree so that its farthest node is as close as possible.
its jobs today
- Rooting both trees at their centre before checking whether two trees have the same shape (tree isomorphism), so the roots match.
- Placing a server or hub in a tree-shaped network so that the farthest node is the fewest hops away.
the weight it carries
Illustrative: a tree of 2·10⁴ nodes (LC 310's limit). BFS from every root: 2·10⁴ × 2·10⁴ = 4·10⁸ steps. Peeling leaves: each node removed once and each edge touched twice — about 2·10⁴ + 2 × (2·10⁴ − 1) ≈ 6·10⁴ steps.
without it
You would try every root with a BFS — quadratic, far too slow at 10⁴+ nodes.
when · why · how
- when
- "Which root gives the smallest height?" or "the middle of the longest path" in a tree.
- why
- Each round removes every leaf, shortening every longest path by one at both ends, so the centre survives last: O(n) instead of O(n²).
- how
int[] degreeand anArrayDeque<Integer>of degree-1 nodes; remove one whole layer per round while more than 2 nodes remain.
02 · plain english
The idea in 30 seconds
Draw an arrow u → v for "u must happen before v". A topological order lists every vertex so all arrows point forward.
Kahn: repeatedly take a vertex with no arrows coming in (nothing left to wait for). DFS: a vertex is finished only after everything it points to — so reverse the finish order.
It exists only if there is no directed cycle. Both methods detect the cycle for free — that is half of all interview questions on this topic.
picture this
A Gradle or Maven build: a module compiles only after the modules it depends on. The build tool keeps picking a module whose dependencies are already built, builds it, and repeats. If module A needs B and B needs A, nothing can ever start — the build stops with a cycle error.
03 · remember
What you must know
- A DAG (directed acyclic graph) always has at least one topological order; a graph with a directed cycle has none.
- Kahn's algorithm (BFS): compute in-degrees, queue every in-degree-0 vertex, pop → output → decrement neighbours → enqueue new zeros. O(V + E).
- Cycle check with Kahn: if fewer than V vertices were output, the rest are stuck on (or behind) a cycle.
- DFS method: three colours — white (unvisited), gray (on the current path), black (finished). Push a vertex when it turns black; the reversed push order is a topological order.
- Cycle check with DFS: an edge into a gray vertex is a back edge = a cycle. An edge into a black vertex is harmless.
- Orders are usually not unique. Need the lexicographically smallest one? Swap Kahn's queue for a
PriorityQueue→ O(V log V + E). Unique order ⇔ Kahn's queue never holds 2+ vertices. - Process vertices in topological order and you can do DP on a DAG: longest path / critical path in O(V + E) — NP-hard on general graphs, easy on DAGs.
- Watch the edge direction in the input: LeetCode's
[course, pre]means the arrow goes pre → course.
04 · see it
Play with it in 3D
Press Space to play, ←/→ to step, drag to move, ? for all shortcuts. Try your own input.
Kahn's algorithm — in-degree array + queue
Numbers under each vertex are in-degrees. A vertex turns violet when it reaches 0 and joins the queue; lime when output.
DFS — colours, call stack and the finished stack
Amber = on the current path (gray). A vertex is pushed only once everything it points to is finished. Pop the stack to read the order.
Cycle detection — spotting the back edge
Course Schedule input `[course, pre]`. When an edge points at an amber (gray) vertex, the red loop is the cycle.
Lexicographically smallest order — Kahn with a min-heap
Same graph as Kahn above, but the ready set is a PriorityQueue: 0 now beats 2, giving 4, 5, 0, 2, 3, 1.
05 · cost
Time & space, explained
| Time | Extra space | why | |
|---|---|---|---|
| Kahn (queue) | O(V + E) | O(V + E) | Each vertex is enqueued/polled once, each edge decremented once. Space is the adjacency list plus in-degree array and queue. |
| DFS + reverse post-order | O(V + E) | O(V + E) | Each vertex turns gray then black once, each edge is looked at once. Recursion depth can reach V. |
| Kahn with PriorityQueue (smallest order) | O(V log V + E) | O(V + E) | V offers and V polls on a heap cost O(log V) each; edges are still O(1) each. |
| Naive: repeatedly scan for a free vertex | O(V · (V + E)) | O(V) | Up to V rounds, and each round rescans every vertex and edge to find one with no remaining prerequisite. |
| Longest path in a DAG (topo order + DP) | O(V + E) | O(V + E) | One topological pass; each edge relaxes its target once. On general graphs the same problem is NP-hard. |
| Time | Extra space | why | |
|---|---|---|---|
| Kahn: output count < V | O(V + E) | O(V + E) | Vertices on a cycle never reach in-degree 0, so they are never output. |
| DFS with three colours | O(V + E) | O(V) | Colour array + recursion stack; one back edge to a gray vertex proves a cycle. |
| DFS with only visited[] (two states) | wrong | — | It can't tell "on my current path" (cycle) from "finished earlier" (fine) — it reports false cycles on diamonds like 0→1, 0→2, 1→3, 2→3. |
06 · java
Raw vs Collections Framework
Kahn's algorithm: Collections vs plain arrays
class KahnRaw { /** Same algorithm with plain arrays only: an int[] works as the queue (head/tail indices). */ static int[] topoSort(int n, int[][] edges) { int[] outCount = new int[n]; int[] indegree = new int[n]; for (int[] e : edges) { outCount[e[0]]++; indegree[e[1]]++; } int[][] adj = new int[n][]; for (int v = 0; v < n; v++) adj[v] = new int[outCount[v]]; int[] fill = new int[n]; for (int[] e : edges) adj[e[0]][fill[e[0]]++] = e[1]; int[] queue = new int[n]; // every vertex enters at most once int head = 0, tail = 0; for (int v = 0; v < n; v++) if (indegree[v] == 0) queue[tail++] = v; while (head < tail) { int u = queue[head++]; for (int v : adj[u]) { if (--indegree[v] == 0) queue[tail++] = v; } } // The queue array IS the topological order (in the order vertices were enqueued). return tail == n ? queue : new int[0]; }}class Kahn { /** edges[i] = {from, to}: "from" must come before "to". Returns [] if there is a cycle. */ static int[] topoSort(int n, int[][] edges) { List<List<Integer>> adj = new ArrayList<>(); for (int i = 0; i < n; i++) adj.add(new ArrayList<>()); int[] indegree = new int[n]; for (int[] e : edges) { adj.get(e[0]).add(e[1]); indegree[e[1]]++; } Deque<Integer> queue = new ArrayDeque<>(); for (int v = 0; v < n; v++) { if (indegree[v] == 0) queue.offer(v); } int[] order = new int[n]; int idx = 0; while (!queue.isEmpty()) { int u = queue.poll(); order[idx++] = u; for (int v : adj.get(u)) { indegree[v]--; if (indegree[v] == 0) queue.offer(v); } } return idx == n ? order : new int[0]; }}- Each vertex enters the queue at most once, so a plain
int[n]withhead/tailindices is a complete queue — and it is the output order. - The Collections version is easier to write under pressure.
ArrayDequebeatsLinkedListas a queue (no node objects) andStack(synchronized, legacy). - Building
int[][]adjacency needs two passes (count, then fill) — faster in hot loops, but more code. Use it when E is in the millions. - Return an empty array (or
false) when output size < V — that is the cycle check.
DFS: three colours and a finished stack
class DfsTopo { static final int WHITE = 0, GRAY = 1, BLACK = 2; /** Reverse post-order DFS. Returns [] if a cycle (back edge to a GRAY vertex) exists. */ static int[] topoSort(int n, int[][] edges) { List<List<Integer>> adj = new ArrayList<>(); for (int i = 0; i < n; i++) adj.add(new ArrayList<>()); for (int[] e : edges) adj.get(e[0]).add(e[1]); int[] color = new int[n]; Deque<Integer> finished = new ArrayDeque<>(); // used as a stack for (int v = 0; v < n; v++) { if (color[v] == WHITE && !dfs(v, adj, color, finished)) return new int[0]; } int[] order = new int[n]; for (int i = 0; i < n; i++) order[i] = finished.pop(); return order; } private static boolean dfs(int u, List<List<Integer>> adj, int[] color, Deque<Integer> finished) { color[u] = GRAY; for (int v : adj.get(u)) { if (color[v] == GRAY) return false; if (color[v] == WHITE && !dfs(v, adj, color, finished)) return false; } color[u] = BLACK; finished.push(u); return true; }}finished.push(u)happens after all of u's neighbours are done. Popping the stack therefore lists u before everything it points to.- Using
ArrayDequeas a stack:push/popwork on the head. Don't usejava.util.Stack— it's a synchronizedVector. - Java's default thread stack overflows around 10⁴–10⁵ nested calls. For big inputs prefer Kahn (iterative) or run DFS with an explicit stack.
Smallest order: PriorityQueue instead of a queue
class SmallestOrder { /** Kahn's algorithm with a min-heap: always take the smallest ready vertex. */ static int[] topoSort(int n, int[][] edges) { List<List<Integer>> adj = new ArrayList<>(); for (int i = 0; i < n; i++) adj.add(new ArrayList<>()); int[] indegree = new int[n]; for (int[] e : edges) { adj.get(e[0]).add(e[1]); indegree[e[1]]++; } PriorityQueue<Integer> ready = new PriorityQueue<>(); // min-heap for (int v = 0; v < n; v++) { if (indegree[v] == 0) ready.offer(v); } int[] order = new int[n]; int idx = 0; while (!ready.isEmpty()) { int u = ready.poll(); order[idx++] = u; for (int v : adj.get(u)) { indegree[v]--; if (indegree[v] == 0) ready.offer(v); } } return idx == n ? order : new int[0]; }}new PriorityQueue<>()onIntegeris a min-heap by natural order. For strings or custom ranking pass aComparator.- Only Kahn can do this directly: DFS has no natural point to "pick the smallest ready vertex".
DP on a DAG: longest (critical) path
class LongestPath { /** Weighted DAG: edges[i] = {from, to, weight}. Longest path length starting anywhere. */ static long longestPath(int n, int[][] edges) { List<List<int[]>> adj = new ArrayList<>(); for (int i = 0; i < n; i++) adj.add(new ArrayList<>()); int[] indegree = new int[n]; for (int[] e : edges) { adj.get(e[0]).add(new int[] {e[1], e[2]}); indegree[e[1]]++; } long[] best = new long[n]; // best[v] = longest path that ENDS at v Deque<Integer> queue = new ArrayDeque<>(); for (int v = 0; v < n; v++) if (indegree[v] == 0) queue.offer(v); long answer = 0; while (!queue.isEmpty()) { int u = queue.poll(); // all of u's predecessors are final now answer = Math.max(answer, best[u]); for (int[] edge : adj.get(u)) { int v = edge[0]; best[v] = Math.max(best[v], best[u] + edge[1]); // relax "upwards" if (--indegree[v] == 0) queue.offer(v); } } return answer; }}- When a vertex is polled, all its predecessors are final — so
best[u]is final too. That's why DP in topological order is correct. - Use
longwhen weights can add up beyond ~2·10⁹. - The same loop with
mingives shortest paths in a DAG — even with negative weights, in O(V + E).
| call | does | cost |
|---|---|---|
new ArrayDeque<Integer>() | Queue for Kahn (offer/poll) or stack for DFS (push/pop) | O(1) amortized per op |
new PriorityQueue<Integer>() | Min-heap of ready vertices → smallest order | O(log n) offer/poll |
List<List<Integer>> adj = new ArrayList<>() | Adjacency list; add n empty lists first | O(1) amortized add |
map.computeIfAbsent(k, x -> new ArrayList<>()).add(v) | Adjacency when vertices are strings/chars | O(1) average |
map.merge(k, 1, Integer::sum) | Increment an in-degree stored in a map | O(1) average |
int[] indegree = new int[n] | In-degree counters, all start at 0 | O(n) |
Arrays.fill(color, 0) | Reset colours between test cases | O(n) |
Any valid order, or "is it possible?"
→ Kahn with ArrayDeque
Iterative (no stack overflow), cycle check is just a counter.
Lexicographically smallest order
→ Kahn with PriorityQueue
Always pick the smallest ready vertex: O(V log V + E).
Which vertices are on / lead into a cycle
→ DFS colours, or Kahn on the reversed graph
Gray-at-the-end vertices are exactly the unsafe ones (LC 802).
Longest path, earliest finish time, max chain
→ Kahn + DP (or DFS + memo)
Topological order guarantees predecessors are final before you use them.
Peel a tree from the outside in (centres)
→ Kahn on leaves (degree 1)
Same idea, undirected: repeatedly remove the vertices with nothing "behind" them.
07 · recognise
Spot the pattern
"prerequisites", "depends on", "must happen before", "build order"
Directed graph + topological sort
"Is it possible to finish / schedule everything?"
Cycle detection: Kahn count or DFS gray back edge
Derive an ordering from sorted examples (alien dictionary)
Compare adjacent items, first difference = an edge, then topo sort
"Minimum time when tasks run in parallel"
Longest path in a DAG (Kahn + DP)
"Smallest / lexicographically first valid order"
Kahn with a PriorityQueue
Strictly increasing moves on a grid (longest increasing path)
Implicit DAG: memoised DFS or peel by in-degree
"Find the centre / root that minimises height" of a tree
Peel leaves layer by layer (Kahn on degree 1)
08 · traps & tricks
Mistakes to avoid, tricks to use
traps
- Reversing edge direction:
[course, pre]means pre → course. Draw one edge on paper before coding. - Using a two-state
visited[]for directed cycle detection — a diamond (two paths into one vertex) looks like a cycle. You need gray vs black. - Forgetting to reverse the DFS post-order (or reversing a Kahn order, which is already correct).
- Forgetting vertices with no edges at all — loop over all V vertices when seeding / starting DFS.
- Counting duplicate edges twice in in-degree but adding them once to adjacency (or vice versa) — keep both consistent (Alien Dictionary uses a
Set). - Deep recursion on 10⁵ vertices →
StackOverflowErrorin Java. Switch to Kahn. - Returning a partial order when a cycle exists. Check
count == Vbefore returning.
tricks
- Default to Kahn in interviews: iterative, cycle check is one line, and it extends to PriorityQueue and DP.
- Say the invariant: "a vertex enters the queue exactly when all its prerequisites are already output."
- Dry-run on a 4-vertex diamond (0→1, 0→2, 1→3, 2→3) — it catches both direction bugs and visited-vs-colour bugs. Bas itna yaad rakho: draw it first, code second.
- Mention both methods and the trade-off (BFS vs recursion depth) — interviewers love hearing you know two ways.
- If the answer must be "any valid order", say so — and validate with a check that every edge points forward.
09 · why & how
Questions you might be too shy to ask
What exactly is in-degree, and why does 0 mean "ready"?
In-degree = how many arrows point into a vertex = how many things it is still waiting for. For prerequisites [[1,0],[2,0],[2,1]] (arrows 0→1, 0→2, 1→2): course 0 has in-degree 0, course 1 has 1, course 2 has 2. In-degree 0 means nothing is pending, so it can go next. Each time a vertex is output, its neighbours lose one pending prerequisite: indegree[v]--.
Isn't Kahn just BFS? Can I use a normal BFS order from the start vertex?
No — plain BFS orders vertices by distance from the start, not by dependencies. Edges 0→1, 0→2, 2→1: if 0's list is [1, 2], BFS from 0 visits 1 before 2 (both are one hop away), but 2 → 1 says 2 must come first. Kahn looks like BFS, but a vertex joins the queue only when its in-degree hits 0 — when all its prerequisites are done, not when it is first seen.
Why does a cycle make a topological order impossible?
Take a cycle a → b → c → a. In any list, one of these three appears first, say a. But c → a says c must come before a. Contradiction — so no order can satisfy all edges.
Why does reversing the DFS finish order work?
When u finishes, every vertex u points to has already finished — it was either black before we got there or we finished it during u's own DFS (it can't be gray, or that would be a cycle). So for every edge u → v, v is pushed before u. Reversing puts u before v — exactly what we need.
Kahn or DFS — which should I write?
Kahn by default: it's iterative (no stack overflow), the cycle check is a counter, and it upgrades easily to PriorityQueue (smallest order) and DP (longest path). Use DFS when you already have a DFS skeleton or need per-vertex results like "safe" states.
Why three colours? Isn't visited[] enough?
In a directed graph, reaching a vertex you finished earlier is fine (e.g. two courses share a prerequisite). Only reaching a vertex that is still on the current path (gray) means you went in a circle. Two states can't tell those apart.
What if the graph is disconnected, or some vertex has no edges at all?
Still fine — the order just mixes the pieces in some way. Kahn: seed the queue with every in-degree-0 vertex, not only vertex 0; a lonely vertex has in-degree 0 and comes out right away. DFS: start a new DFS from every vertex that is still white. Miss this and your output is shorter than V, which looks like a fake cycle.
Can I print the actual cycle, not just say "there is one"?
Yes, with DFS and a parent[] array. When an edge u → v hits a gray v, the cycle is v → … → u → v: walk parent back from u until you reach v. Kahn can't do this cleanly — its leftover vertices are the ones on a cycle plus everything that depends on one (can be reached from it).
How many valid orders can there be?
Possibly a huge number — a graph with no edges has V! orders. Counting them exactly is a known hard problem in general (#P-complete, proved by Brightwell and Winkler in 1991). The order is unique exactly when Kahn's queue never holds more than one vertex at a time.
Does the order from my code have to match LeetCode's example exactly?
No — problems like Course Schedule II accept any valid order. The judge checks that every prerequisite comes first. Only problems that ask for a specific order (e.g. smallest) need a PriorityQueue.
Alien Dictionary: why only the first differing letter, and only neighbouring words?
Think of a normal dictionary: "cat" comes before "dog" only because c < d. The letters after that (a vs o) were never compared, so they prove nothing. Neighbours are enough because sorted order chains: if w1 ≤ w2 and w2 ≤ w3, then w1 ≤ w3. So any letter order that keeps every neighbouring pair in order keeps the whole list sorted. Comparing far-apart pairs only repeats what the neighbour rules already imply.
Find Eventual Safe States: why reverse the edges before running Kahn?
Safety depends on where you can go: a node is safe when every outgoing edge leads to a safe node. Kahn frees a vertex when its incoming count hits 0. Flip every edge and "all my exits are safe" becomes "all my incoming edges are done" — terminal nodes become the starting sources. The counter is simply outdegree[u] from the original graph.
Does topological sort work on undirected graphs?
Not as such — an undirected edge has no "before". But the same peeling idea works on trees: Minimum Height Trees removes degree-1 leaves layer by layer, exactly like Kahn removes in-degree-0 vertices.
Minimum Height Trees: why peel a whole layer of leaves at once, not one leaf at a time?
Both ends of the longest path must shrink at the same speed. Path 0-1-2-3-4, one leaf at a time: remove 0, then 4, then 1 — now only 2 and 3 remain, so you stop and return [2, 3]. The right answer is [2]. Layer by layer: remove {0, 4}, then {1, 3}, and only 2 is left. Dhyan do: count the current layer's size before you start removing it.
10 · practice
Interview problems
Every problem has brute → better → optimal solutions in Java, with complexity and a dry run.
- #207
- #210
- #953Verifying an Alien DictionaryEasyMetaAmazon
- #269
- #802Find Eventual Safe StatesMediumAmazonGoogle
- #2050Parallel Courses IIIHardGoogleGoldman SachsAmazon
- #310
- #329
- #2392Build a Matrix With ConditionsHardGoogle
11 · revise
The whole chapter on one screen
9 cards, about a minute. Read them the night before an interview, or right after the chapter to lock it in.
- what it is
A topological order lists every vertex so all arrows point forward. It exists exactly when the directed graph has no cycle (a DAG).
- use it when
"Prerequisites", "depends on", "build order", "is it possible to finish?" — or an order hidden in sorted examples (alien dictionary).
- template
Kahn: count in-degrees → queue every 0 → poll, output, decrement neighbours, queue new 0s. Output count < V ⇒ cycle.
- cost
Kahn and DFS: O(V + E) time and space. Smallest order with a
PriorityQueue: O(V log V + E). Longest path on a DAG: O(V + E). - trap
[course, pre]means the arrow goes pre → course. Draw one edge on paper before you code. - trap
Directed cycle check needs three colours. A plain
visited[]calls a harmless diamond a cycle. - trap
DFS gives the finish order reversed — pop the stack. Kahn's order is already correct; don't reverse it.
- pro tip
Default to Kahn in Java: no recursion depth limit, a one-line cycle check, and it upgrades to min-heap order and DP.
- pro tip
Process vertices in topological order and DP gets easy: critical path, earliest finish, counting paths.
12 · recall
Flip, remember, repeat
When does a topological order exist?
Question 1: When does a topological order exist?
13 · check yourself
Quick quiz
Kahn's algorithm finished and output 5 of 7 vertices. What does that mean?
During directed DFS you reach a vertex that is BLACK. What do you do?
Edges: 0→1, 0→2, 1→3, 2→3. Which is NOT a valid topological order?
You need the lexicographically smallest topological order. What changes in Kahn?
Course Schedule gives prerequisites [a, b]. Which edge do you add?
Why does memoised DFS in Longest Increasing Path need no visited[] array?
What is the time complexity of Kahn's algorithm?
14 · learn more
A second teacher, if you want one
- ▶ videoG-22. Kahn's Algorithm | Topological Sort Algorithm | BFS ↗Striver (take U forward) · EnglishKahn's algorithm with in-degrees and a queue. This BFS version is the one behind Course Schedule.
- ▶ videoTopological Sort Algorithm | Graph Theory ↗William Fiset · EnglishThe DFS version, animated: finish a node, put it in front. Also shows why the order only exists on a DAG.
- ▶ videoCourse Schedule | Apple | Microsoft | Amazon | BFS | Graph Concepts & Qns - 12 | Leetcode 207 ↗codestorywithMIK · HindiCourse Schedule solved with Kahn's algorithm, start to finish. It's the most-asked topological sort problem.