Topological Sort Algorithm

Other names:
topological sorting, topological ordering

In short: Topological sort is an algorithm that orders the nodes of a directed acyclic graph (DAG) so that every node comes before all the nodes it points to. Kahn's algorithm (a BFS that repeatedly takes nodes with no incoming edges) and depth-first search both find one in O(V + E) time, and both detect cycles, which make a topological ordering impossible.

Topological sort is an algorithm that takes a directed graph and returns an array of its nodes where every node appears before all the nodes it points to. An ordering like that only exists if the graph has no cycles (it's a directed acyclic graph, or DAG). The two standard ways to find one—Kahn's algorithm and depth-first search—both run in O(V + E) time.

The ordering of the nodes in the array is called a topological ordering.

Kahn's algorithm (BFS) DFS-based
time O(V + E) O(V + E)
space O(V) O(V)
detects cycles? Yes—some nodes never get down to zero incoming edges Yes—the search runs into a node that's still on its current path

(V is the number of nodes, or vertices, and E is the number of edges.)

Here's an example:

The messy directed graph where node 1 points to nodes 2 and 3. Node 2 points to node 4 and 5, and node 4 points to node 5.

Since node 1 points to nodes 2 and 3, node 1 appears before them in the ordering. And, since nodes 2 and 3 both point to node 4, they appear before it in the ordering.

The topologically sorted directed graph where node 1 points to nodes 2 and 3. Node 2 points to node 4 and 5, and node 4 points to node 5.

So [1, 2, 3, 4, 5] would be a topological ordering of the graph.

Can a graph have more than one valid topological ordering? Yep! In the example above, [1, 3, 2, 4, 5] works too.

Cyclic Graphs

Look at this directed graph with a cycle:

A points to B, B points to C and C points to D which points to B.

The cycle creates an impossible set of constraints—B has to be before and after D in the ordering.

As a rule, cyclic graphs don't have valid topological orderings. That makes topological sort a handy way to detect cycles, too: both of the algorithms below notice when they run into one.

What about undirected graphs? An undirected edge goes both ways, so its two nodes would each have to come before the other. Topological sort only makes sense for directed graphs.

Kahn's Algorithm

How can we produce a topological ordering for this directed graph?

An example of the directed graph with five nodes linked between themselves.

Well, let's focus on the first node in the topological ordering. That node can't have any incoming directed edges; it must have an indegree of zero.

Why?

Because if it had incoming directed edges, then the nodes pointing to it would have to come first.

The same example of the directed graph with bolded first node which has two possible incoming edges pointed to him.

So, we'll find a node with an indegree of zero and add it to the topological ordering.

The same example of the directed graph with bolded first node.

That covers the first node in our topological ordering. What about the next one?

Once a node is added to the topological ordering, we can take the node, and its outgoing edges, out of the graph.

An example of the directed graph with removed first node.

Then, we can repeat our earlier approach: look for any node with an indegree of zero and add it to the ordering.

This is a common algorithm design pattern:

  1. Figure out how to get the first thing.
  2. Remove the first thing from the problem.
  3. Repeat.

Here's what this looks like on our graph. We'll grab a node with an indegree of 0, add it to our topological ordering and remove it from the graph:

An example of the directed graph with removed first node and bolded the second element.

and repeat

An example of the directed graph with removed two nodes.

and repeat

An example of the directed graph with removed three nodes.

until we're

An example of the directed graph with removed four nodes.

out of nodes.

This approach is called Kahn's algorithm, after A. B. Kahn, who described it in 1962. It's sometimes called the "BFS" approach to topological sort, because it works outward from the nodes with no prerequisites, a bit like a breadth-first search.

Step by step

Here's that same run as a table. Our graph's edges are B→C, B→E, C→D, E→A, E→C, A→C, and A→D, so the starting indegrees are A: 1, B: 0, C: 3, D: 2, and E: 1.

Step Node with indegree 0 Indegree updates Topological ordering so far
1 B C: 3 → 2, E: 1 → 0 B
2 E A: 1 → 0, C: 2 → 1 B, E
3 A C: 1 → 0, D: 2 → 1 B, E, A
4 C D: 1 → 0 B, E, A, C
5 D (no outgoing edges) B, E, A, C, D

Notice that at every step, exactly one node had an indegree of zero, so we never had a choice to make. That means B, E, A, C, D is the only valid topological ordering for this graph. In general, a topological ordering is unique exactly when every pair of neighbors in the ordering is connected by an edge (here: B→E, E→A, A→C, and C→D).

Implementing Kahn's Algorithm

We'll use the strategy we outlined above:

  1. Identify a node with no incoming edges.
  2. Add that node to the ordering.
  3. Remove it from the graph.
  4. Repeat.

We'll keep looping until there aren't any more nodes with indegree zero. This could happen for two reasons:

  • There are no nodes left. We've taken all of them out of the graph and added them to the topological ordering.
  • There are some nodes left, but they all have incoming edges. This means the graph has a cycle, and no topological ordering exists.

One small tweak. Instead of actually removing the nodes from the graph (and destroying our input!), we'll use a hash map to track each node's indegree. When we add a node to the topological ordering, we'll decrement the indegree of that node's neighbors, representing that those nodes have one fewer incoming edges.

Any node with an indegree of zero is safe to take next, so it doesn't matter whether we hold those nodes in a queue or a stack. Different choices can just produce different (equally valid) orderings.

Let's code it up!

import java.util.Map; import java.util.Queue; import java.util.List; import java.util.ArrayList; import java.util.HashMap; import java.util.ArrayDeque; private static String[] topologicalSort(Map<String, String[]> digraph) { // digraph is a map: // key: a node // value: an array of adjacent neighboring nodes // construct a hash mapping nodes to their indegrees Map<String, Integer> indegrees = new HashMap<>(); for (String node : digraph.keySet()) { indegrees.put(node, 0); } for (Map.Entry<String, String[]> entry : digraph.entrySet()) { String node = entry.getKey(); for (String neighbor : entry.getValue()) { indegrees.put(neighbor, indegrees.get(neighbor) + 1); } } // track nodes with no incoming edges Queue<String> nodesWithNoIncomingEdges = new ArrayDeque<>(); for(Map.Entry<String, Integer> entry : indegrees.entrySet()) { if (entry.getValue() == 0) { nodesWithNoIncomingEdges.add(entry.getKey()); } } // initially, no nodes in our ordering List<String> topologicalOrdering = new ArrayList<>(); // as long as there are nodes with no incoming edges that can be // added to the ordering while (nodesWithNoIncomingEdges.size() > 0) { // add one of those nodes to the ordering String node = nodesWithNoIncomingEdges.poll(); topologicalOrdering.add(node); // decremenet the indegree of that node's neighbors for (String neighbor : digraph.get(node)) { indegrees.put(neighbor, indegrees.get(neighbor) - 1); if (indegrees.get(neighbor) == 0) { nodesWithNoIncomingEdges.add(neighbor); } } } // we've run out of nodes with no incoming edges // did we add all the nodes or find a cycle? if (topologicalOrdering.size() != digraph.size()) { throw new IllegalArgumentException("Graph has a cycle! No topological ordering exists."); } return topologicalOrdering.toArray(new String[topologicalOrdering.size()]); }

Topological Sort Using DFS

There's a second classic approach, built on depth-first search.

Here's the key insight. In a DFS, we don't finish a node until we've finished every node we can reach from it. So by the time a node finishes, every node that has to come after it in the ordering has already finished.

That means if we write down the nodes in the order they finish, we get a topological ordering backwards. Reverse it, and we're done.

To catch cycles, we'll keep track of one of three states for each node:

  • Unvisited: we haven't reached it yet.
  • Visiting: it's on our current DFS path. We've started exploring from it, but we haven't finished.
  • Visited: we've finished it, along with everything reachable from it.

If our search ever reaches a node that's still visiting, we've followed edges in a loop back onto our own path. That's a cycle, so no topological ordering exists.

Why do we need two "seen" states instead of one? Running into a visited node is totally fine—it just means two paths lead to the same place (like B→C and B→E→C in our example). Only a node that's still visiting means we've looped back on ourselves.

import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; private enum NodeState { VISITING, VISITED } private static String[] topologicalSortDfs(Map<String, String[]> digraph) { // digraph is a map: // key: a node // value: an array of adjacent neighboring nodes // nodes we haven't reached yet aren't in nodeStates Map<String, NodeState> nodeStates = new HashMap<>(); // nodes in the order we finish them // (a topological ordering, but backwards) List<String> finishedNodes = new ArrayList<>(); for (String node : digraph.keySet()) { visit(node, digraph, nodeStates, finishedNodes); } // reverse the finishing order to get a topological ordering Collections.reverse(finishedNodes); return finishedNodes.toArray(new String[finishedNodes.size()]); } private static void visit(String node, Map<String, String[]> digraph, Map<String, NodeState> nodeStates, List<String> finishedNodes) { if (nodeStates.get(node) == NodeState.VISITED) { return; // already finished this node and everything after it } if (nodeStates.get(node) == NodeState.VISITING) { // we looped back to a node on our current path throw new IllegalArgumentException("Graph has a cycle! No topological ordering exists."); } nodeStates.put(node, NodeState.VISITING); for (String neighbor : digraph.get(node)) { visit(neighbor, digraph, nodeStates, finishedNodes); } nodeStates.put(node, NodeState.VISITED); // everything reachable from node has already finished, // so node belongs in front of all of it finishedNodes.add(node); }

Since this version is recursive, the call stack can get as deep as the longest path in the graph—up to V calls. On really big graphs that can cause a stack overflow, so Kahn's algorithm (or a DFS that uses an explicit stack) is the safer choice there.

Time and Space Complexity

Breaking Kahn's algorithm into chunks, we:

  • Determine the indegree for each node. We start every node's count at zero and then look at each directed edge once, so this is O(V + E) time (where V is the number of nodes and E is the number of edges).
  • Find nodes with no incoming edges. This is a simple loop through all the nodes with some number of constant-time appends. O(V) time.
  • Add nodes until we run out of nodes with no incoming edges. This loop could run once for every node—O(V) times. In the body, we:
    • Do two constant-time operations on an array to add a node to the topological ordering.
    • Decrement the indegree for each neighbor of the node we added. Over the entire algorithm, we'll end up doing exactly one decrement for each edge, making this step O(E) time overall.
  • Check if we included all nodes or found a cycle. This is a fast O(1) comparison.

All together, the time complexity is O(V + E).

That's the fastest time we can expect, since we'll have to look at all the nodes and edges at least once.

What about space complexity? Here are the data structures we created:

  • indegrees—this has one entry for each node in the graph, so it's O(V) space.
  • nodesWithNoIncomingEdges—in a graph with no edges, this would start out containing every node, so it's O(V) space in the worst case.
  • topologicalOrdering—in a graph with no cycles, this will eventually have every node. O(V) space.

All in all, we have three structures and they're all O(V) space. Overall space complexity: O(V).

This is the best space complexity we can expect, since we must allocate a return array which costs O(V) space itself.

What about the DFS version?

It's also O(V + E) time. Each node goes from unvisited to visiting to visited exactly once, and we only loop through a node's neighbors the first time we visit it, so we follow each edge once. Reversing the finished nodes at the end is another O(V).

And it's O(V) space: the node states, the list of finished nodes, and the call stack (up to V calls deep) are each O(V).

Kahn's Algorithm vs. DFS: Which Should You Use?

Both run in O(V + E) time and O(V) space, and both detect cycles. The differences are practical:

Kahn's algorithm DFS-based
Main idea Repeatedly take a node with no remaining incoming edges Record nodes as their DFS finishes, then reverse that order
Data structures Indegree counts, plus a queue (or stack) of nodes that are ready Node states, plus recursion (or an explicit stack)
Cycle check The ordering ends up with fewer nodes than the graph The search reaches a node that's still "visiting"
Recursion None, so no risk of a stack overflow The recursive version can overflow the stack on very deep graphs
Handy extras Processes nodes in "waves," which helps with scheduling questions. Swap the queue for a priority queue to get the alphabetically smallest ordering. Easy to bolt onto a DFS you're already writing

In a coding interview, either one is a great answer. Kahn's algorithm is a good default, since it's iterative and its cycle check is just a count.

Uses

The most common use for topological sort is ordering steps of a process where some of the steps depend on each other.

As an example, when making chocolate bundt cake,

  • The ingredients have to be mixed before going in the bundt pan.
  • The bundt pan has to be greased and floured before the batter can be poured in.
  • The oven has to be preheated before the cake can bake.
  • The cake has to be baked before it cools.
  • The cake has to cool before it can be iced.

While we've chosen a fun example here, the same logic applies to any set of tasks with dependencies, like building components in a large software project, performing data analysis in Map-Reduce job, or bringing up hardware components at boot time. (For example, the mother board has to initialize the hard drive before the BIOS tries to load the bootloader from disk.)

This process can be represented as a directed graph. Each step is a node, and we'll add directed edges between nodes to represent that one step has to be done before another.

The messy directed graph represented as a process of making chocolate bundt cake.

Once we have our dependencies represented using a directed graph, we can use topological sort to provide a valid ordering to tackle all the steps.

The topologically sorted directed graph represented as a process of making chocolate bundt cake.

Topological Sort Interview Questions

Topological sort comes up in interviews whenever you see dependencies: prerequisites, build steps, or anything that has to happen "before" something else. A few classics:

  • Course schedule. Given a list of courses and their prerequisites, can you finish all of them? Make each course a node, and add an edge from each prerequisite to the course that requires it. You can finish everything exactly when the graph has no cycle—that is, when a topological sort includes every course. A common follow-up asks for an order to take the courses in. That's the topological ordering itself.

  • Build order. Given a list of projects and the projects each one depends on, find an order to build them so every project's dependencies get built first. Build tools and package managers solve this exact problem.

  • Alien dictionary. Given a list of words sorted in an unknown alphabet, figure out the order of the letters. Compare each pair of neighboring words: the first position where they differ tells you that one letter comes before another, so add an edge between those two letters. Then topologically sort the letters. (Watch out for invalid input, like "abc" listed before "ab"—a word can't come before its own prefix.)

  • Parallel scheduling. If you can take any number of courses at once, what's the fewest number of semesters you need? Run Kahn's algorithm in "waves," taking every node with an indegree of zero at once. The number of waves is your answer.

  • Shortest paths in a DAG. Go through the nodes in topological order, updating the best known distance to each node's neighbors as you go. That finds the shortest paths from a starting node in O(V + E) time—faster than Dijkstra's algorithm, and it works even when some edge weights are negative.

Frequently Asked Questions

What is topological sort?

Topological sort is an algorithm that orders the nodes of a directed acyclic graph (DAG) so that for every edge from node A to node B, A comes before B. It's used to schedule tasks that depend on each other, like courses with prerequisites or build steps.

Is topological sort BFS or DFS?

It can be either. Kahn's algorithm is the BFS-style approach: it repeatedly takes a node with no incoming edges and decrements its neighbors' indegrees. The DFS approach records each node when its depth-first search finishes, then reverses that order. Both run in O(V + E) time.

What is Kahn's algorithm?

Kahn's algorithm finds a topological ordering by counting each node's incoming edges (its indegree), starting with the nodes whose indegree is zero, and repeatedly adding one of those nodes to the ordering and decrementing the indegrees of its neighbors. If some nodes never reach an indegree of zero, the graph has a cycle.

What is the time complexity of topological sort?

O(V + E), where V is the number of nodes (vertices) and E is the number of edges, for both Kahn's algorithm and the DFS approach. Each node and each edge is processed once. Both use O(V) extra space.

Can you topologically sort a graph with a cycle?

No. A cycle would force some node to come both before and after another node, so only directed acyclic graphs (DAGs) have a topological ordering. That makes topological sort a cycle detector too: Kahn's algorithm leaves some nodes out of the ordering, and the DFS approach reaches a node that's still on its current path.

Is a topological ordering unique?

Usually not. A DAG can have many valid topological orderings. The ordering is unique only when every pair of consecutive nodes in it is connected by an edge, which in Kahn's algorithm means there's never more than one node with an indegree of zero to choose from.

Can you topologically sort an undirected graph?

No. Topological sort needs directed edges. An undirected edge goes both ways, so each of its two nodes would have to come before the other.

What interview problems use topological sort?

Course Schedule (can you finish every course given the prerequisites?), Course Schedule II (return a valid order), Alien Dictionary (find the letter order of an unknown alphabet), build order and dependency resolution, and parallel scheduling problems like finding the minimum number of semesters.

Last updated: October 21, 2026

What's next?

If you're ready to start applying these concepts to some problems, check out our mock coding interview questions.

They mimic a real interview by offering hints when you're stuck or you're missing an optimization.

Try some questions now

. . .