A topological ordering of a directed graph lists the vertices so that every edge has before . It exists iff the graph is a DAG, and it is generally not unique.
Kahn’s algorithm (BFS-based)
Repeatedly remove a vertex with in-degree 0. Doubles as a cycle detector: if fewer than vertices come out, the graph has a cycle.
vector<int> kahn(int n, vector<vector<int>>& adj) {
vector<int> indeg(n, 0), order;
for (int u = 0; u < n; u++)
for (int v : adj[u]) indeg[v]++;
queue<int> q;
for (int i = 0; i < n; i++) if (!indeg[i]) q.push(i);
while (!q.empty()) {
int u = q.front(); q.pop();
order.push_back(u);
for (int v : adj[u])
if (--indeg[v] == 0) q.push(v);
}
return (int)order.size() == n ? order : vector<int>{}; // empty = cycle
}Swap the queue for a priority_queue to get the lexicographically smallest topological order — a very common problem variant.
DFS-based
Push each vertex when its subtree is finished, then reverse.
vector<int> order; vector<int> state; // 0 = white, 1 = grey, 2 = black
bool cycle = false;
void dfs(int u, vector<vector<int>>& adj) {
state[u] = 1;
for (int v : adj[u]) {
if (state[v] == 0) dfs(v, adj);
else if (state[v] == 1) cycle = true; // back edge
}
state[u] = 2;
order.push_back(u);
}
vector<int> topoDFS(int n, vector<vector<int>>& adj) {
state.assign(n, 0); order.clear(); cycle = false;
for (int i = 0; i < n; i++) if (!state[i]) dfs(i, adj);
if (cycle) return {};
reverse(order.begin(), order.end());
return order;
}The three-colour scheme is the point: a grey neighbour means a back edge, hence a cycle. A black neighbour is fine — it is a forward or cross edge.
Which to use
| Need | Use |
|---|---|
| Explicit cycle detection | either — Kahn is more direct |
| Lexicographically smallest order | Kahn with a priority queue |
| Avoiding deep recursion | Kahn |
| Counting the number of topological orders | DP over subsets, |
| Checking uniqueness | Kahn — unique iff the queue never holds two vertices at once |
| Preprocessing inside a DFS-based algorithm | DFS version |
What topological order unlocks
Once vertices are in topological order, any DP over a DAG becomes a single loop:
- Longest / shortest path in a DAG — relax edges in topological order, , and negative weights are fine
- Counting paths between two vertices
- Reachability / transitive closure by processing in reverse order
- Dependency resolution — build systems, course prerequisites, task scheduling
- 2-SAT — assign values in reverse topological order of the condensation
- DP on DAGs generally
Related facts
- A DAG has a unique topological order iff it contains a Hamiltonian path.
- The longest path in a DAG is solvable in linear time (unlike general graphs, where it is NP-hard).
- Mirsky’s theorem: the minimum number of antichains needed to cover a DAG equals the length of its longest chain. Computing it is exactly the longest-path DP.
- Dilworth’s theorem: the minimum number of chains covering a poset equals the size of its largest antichain — computed via bipartite matching.
See also: Kahn’s Algorithm · Cycle Detection · Shortest Paths on DAGs