Skip to main content
Back to the DSA atlas
Graphs Algorithm Intermediate

Topological Sort

Order the nodes of a DAG so that every edge points forward. Essential for dependency resolution, build systems, and scheduling.

graphsdfsdagtopological-sort

Interactive visualization

Use the controls to connect the idea to concrete operations before diving into the full write-up.

ABCDEF
Unvisited Exploring Finished

Family

Graphs

Nodes, edges, traversal, connectivity, and shortest paths.

Builds on

2 topics

Read these first if you want the surrounding context.

Unlocks

0 next topics

Use these follow-ups when you want to apply the current idea in a richer setting.

Learning paths

1

This topic appears in curated progressions so you can keep moving without guessing the next step.

Problem

Given a directed acyclic graph (DAG), produce a linear ordering of its nodes such that for every edge uvu \to v, node uu appears before vv in the ordering.

Intuition

Think of it as a dependency graph. If task A must be done before task B (ABA \to B), then A must appear earlier in the sorted order. Topological sort finds any valid ordering that respects all these constraints.

There can be multiple valid orderings. The graph A → C, B → C can be sorted as either [A, B, C] or [B, A, C] — both are correct because A and B have no ordering constraint between them.

A topological ordering exists if and only if the graph has no cycles. A cycle means A depends on B, B depends on C, C depends on A — there’s no way to order them linearly.

DFS-based approach (reverse post-order)

Run DFS. When a node finishes (all its descendants are fully explored), push it onto a result stack. At the end, the stack — read top to bottom — is a valid topological order.

topo_sort_dfs(graph):
    visited ← {}
    result ← []

    for each node in graph:
        if node not in visited:
            dfs(graph, node, visited, result)

    return reverse(result)

dfs(graph, node, visited, result):
    visited.add(node)
    for neighbor in graph[node]:
        if neighbor not in visited:
            dfs(graph, neighbor, visited, result)
    result.append(node)  ← post-order

Why does this work? A node is appended after all nodes it can reach. So if uvu \to v, then vv finishes before uu, and uu appears before vv in the reversed result.

Kahn’s algorithm (BFS-based)

Kahn’s takes a different angle: start with nodes that have no incoming edges (in-degree 0), process them, remove their outgoing edges, and repeat. If new nodes now have in-degree 0, add them to the queue.

kahn(graph):
    in_degree ← compute in-degrees for all nodes
    queue ← [nodes with in_degree == 0]
    result ← []

    while queue is not empty:
        node ← queue.dequeue()
        result.append(node)

        for neighbor in graph[node]:
            in_degree[neighbor] -= 1
            if in_degree[neighbor] == 0:
                queue.enqueue(neighbor)

    if result.length != total_nodes:
        error "Graph has a cycle"
    return result

Kahn’s has a nice bonus: if the result is shorter than the total number of nodes, you know the graph has a cycle. The DFS approach can detect cycles with the three-color technique (see DFS page).

DFS vs Kahn’s

DFS-basedKahn’s (BFS-based)
Core ideaReverse post-orderPeel off zero-in-degree nodes
Cycle detectionThree-color schemeResult length < node count
ImplementationRecursive, compactIterative, uses in-degree array
DeterminismDepends on neighbor orderDepends on queue processing order

Both are O(V+E)O(V + E). Kahn’s is often preferred in practice because it’s iterative (no stack overflow risk) and the cycle detection is trivial.

Use cases

Build systems. Make, Bazel, Gradle — all resolve task dependencies with topological sort. Compile file A before B if B imports A.

Course prerequisites. Given a list of courses and prerequisites, find a valid order to take them. This is literally topological sort on the prerequisite graph.

Package managers. npm, pip, cargo — install dependencies in the right order.

Spreadsheet evaluation. Cell B1 depends on A1 and A2. Topological sort determines the evaluation order so every cell’s dependencies are computed before the cell itself.

Data pipeline scheduling. ETL jobs where step 3 depends on steps 1 and 2.

Longest path in a DAG

You can find the longest path in a DAG (impossible in general graphs — that’s NP-hard) by processing nodes in topological order and relaxing edges with max instead of min:

for node in topological_order:
    for (neighbor, weight) in graph[node]:
        if dist[node] + weight > dist[neighbor]:
            dist[neighbor] = dist[node] + weight

This is used in critical path analysis (project scheduling) and some DP problems.

Complexity

TimeSpace
DFS-basedO(V+E)O(V + E)O(V)O(V)
Kahn’sO(V+E)O(V + E)O(V)O(V)

Key takeaways

  • Topological sort only works on DAGs — a valid ordering exists if and only if the graph has no cycles.
  • Kahn’s algorithm is usually the safer interview choice — it’s iterative (no stack overflow), and cycle detection is trivial (result length < node count).
  • DFS-based approach uses reverse post-order — a node is appended after all reachable nodes are processed, then the result is reversed.
  • Multiple valid orderings can exist — any two nodes without a directed path between them can appear in either order.
  • Think “dependency resolution” — whenever a problem involves prerequisites, ordering constraints, or scheduling, consider topological sort.

Practice problems

ProblemDifficultyKey idea
Course Schedule🟡 MediumDetect if a valid topological ordering exists (cycle detection)
Course Schedule II🟡 MediumReturn an actual topological ordering using Kahn’s or DFS
Alien Dictionary🔴 HardBuild a graph from character ordering constraints, then topo-sort
Minimum Height Trees🟡 MediumPeel leaves iteratively (Kahn’s-like approach on undirected graph)
Sequence Reconstruction🟡 MediumCheck if a unique topological order exists from subsequences

Relation to other topics

  • DFS — the DFS-based topological sort is a direct application of post-order traversal; cycle detection uses the three-color DFS technique.
  • Shortest/longest path in a DAG — processing nodes in topological order allows single-pass relaxation, replacing Dijkstra for DAGs.
  • Dynamic programming on DAGs — many DP problems on graphs reduce to computing values in topological order, since each node’s answer depends only on already-computed successors.

Build on these first

These topics supply the mental model or underlying structure that this page assumes.

Related directions

These topics live nearby conceptually, even if they are not direct prerequisites.

More from Graphs

Stay in the same family when you want parallel variations of the same mental model.

Paths that include this topic

Follow one of these sequences if you want a guided next step instead of open-ended browsing.

From the blog

Pair the atlas with recent writing from the rest of the site when you want a broader engineering perspective alongside the topic graph.