Topological Sort via DFS Departure Time turns the recursion of a depth-first search into an ordering. The key realization: the order in which DFS finishes vertices, reversed, is a valid topological order of a DAG.
Problem. Given a directed acyclic graph (DAG), return an ordering of its vertices such that for
every edge u → v, u appears before v.
Example: edges A→C, A→D, B→D, B→E, C→F, D→F. One valid answer is [B, E, A, D, C, F] — every arrow
in the graph points forward in this list.
The slow way first
A natural first idea is Kahn's algorithm: repeatedly find a vertex with in-degree 0, output it, and remove its outgoing edges. That works and is O(V + E), but it needs an explicit in-degree table and a queue you keep refilling.
The question to ask: is there an ordering already hidden in a plain DFS? It turns out there is — we just have to watch when each vertex finishes, not when it starts.
The idea: record departure (finish) times
Run a normal DFS. A vertex finishes (we call this its departure time) only after all of its descendants have finished. So a vertex always finishes after everything it can reach. That means: if u → v, then v finishes before u. Sort by descending finish time and every edge points forward.
The clean trick: append each vertex to a list the moment it finishes, then reverse the list at the end (or prepend as you go).
Because a finished vertex can never be reached by a still-unfinished one in a DAG, descending finish time is guaranteed acyclic-consistent.
Walk through it
Step through the animation. DFS starts at A, dives A → C → F. F is a sink, so it finishes first (departure 1) and is prepended. Then C finishes, then D, then A. A fresh DFS starts at B, reaches E, and finally B finishes last (departure 6). The order, built by prepending each finisher, is [B, E, A, D, C, F].
Pseudocode
visited = empty set
order = empty list
for each vertex u in the graph:
if u not visited:
dfs(u)
dfs(u):
mark u visited
for each neighbor v of u:
if v not visited:
dfs(v)
append u to order # u just finished — record departure
return reverse(order) # descending finish timeThe Python solution
def topo_sort(graph):
visited = set()
order = []
def dfs(u):
visited.add(u)
for v in graph[u]:
if v not in visited:
dfs(v)
order.append(u) # u finishes here
for u in graph:
if u not in visited:
dfs(u)
return order[::-1] # reverse finish ordervisitedstops us re-exploring a vertex;ordercollects vertices in finish order.- The inner
dfsrecurses into every unvisited neighbor before doing anything withuitself. - Line 10 is the heart:
order.append(u)runs only after the loop over neighbors completes, souis recorded after all its descendants — that is its departure time. - The outer loop restarts DFS from any vertex that a previous tree never reached (here,
B). - Line 15 reverses the finish order, turning "last to finish first" into "sources first" — the topological order.
Complexity
| Case | Time | Notes |
|---|---|---|
| DFS over the graph | O(V + E) (moderate) | each vertex and edge once |
| Final reverse | O(V) (moderate) | reverse the order list |
O(V) (moderate)Same asymptotic cost as Kahn's algorithm, but it falls out of a plain DFS with no in-degree bookkeeping — just one append at the end of each call.
When this pattern shows up
Whenever a problem needs an ordering that respects dependencies — build/compile order, course prerequisites, task scheduling — reach for a topological sort. The DFS-departure-time version is the shortest to write: recurse, append on the way out, reverse.
This only works on a DAG. If the graph has a cycle, there is no valid topological order at all. To be
safe, track vertices currently on the recursion stack and report a cycle if you revisit one — plain
visited is not enough to detect cycles.
Practice
In the example DAG, which vertex finishes first, and why does that make it appear last in the final order?
1. Why does reversing the DFS finish order give a topological sort?
2. When is a vertex appended to the order list?
3. Why is there an outer loop over all vertices?
4. What is the time complexity of this algorithm?