Topological Sort
Authors: Benjamin Qi, Nathan Chen
Contributors: Michael Cao, Andi Qu, Andrew Wang, Qi Wang, Maggie Liu
Ordering the vertices of a directed acyclic graph such that each vertex is visited before its children.
To review, a directed graph consists of edges that can only be traversed in one direction. Additionally, an acyclic graph defines a graph which does not contain cycles, meaning you are unable to traverse across one or more edges and return to the node you started on. Putting these definitions together, a directed acyclic graph, sometimes abbreviated as DAG, is a graph which has edges which can only be traversed in one direction and does not contain cycles.
Topological Sort
Focus Problem – try your best to solve this problem before continuing!
A topological sort of a directed acyclic graph is a linear ordering of its vertices such that for every directed edge from vertex to vertex , comes before in the ordering.
There are two common ways to topologically sort, one involving DFS and the other involving BFS.
Resources | ||||
---|---|---|---|---|
CSA | interactive, both versions |
DFS
Resources | ||||
---|---|---|---|---|
CPH | example walkthrough | |||
CP2 | code | |||
cp-algo | code |
C++
#include <bits/stdc++.h>using namespace std;#define pb push_backint N; // Number of nodesvector<int> graph[100000], top_sort; // Assume that this graph is a DAGbool visited[100000];void dfs(int node) {
Java
import java.io.*;import java.util.*;public class CourseSchedule {static List<Integer>[] graph;static List<Integer> topo = new ArrayList<Integer>();static int N;static boolean[] visited;public static void main(String[] args) throws Exception {BufferedReader br =
Python
import sysMAX_N = 10**5sys.setrecursionlimit(MAX_N)def dfs(node):for i in graph[node]:if not visited[i]:visited[i] = True
Finding a Cycle
Focus Problem – try your best to solve this problem before continuing!
We can modify the algorithm above to return a directed cycle in the case where a topological sort does not exist. To find the cycle, we add each node we visit onto the stack until we detect a node already on the stack.
For example, suppose that our stack currently consists of and we then visit for some . Then is a cycle. We can reconstruct the cycle without explicitly storing the stack by marking as not part of the stack and recursively backtracking until is reached again.
C++
#include <algorithm>#include <iostream>#include <vector>using namespace std;bool visited[(int)1e5 + 5], on_stack[(int)1e5 + 5];vector<int> adj[(int)1e5 + 5];vector<int> cycle;int N, M;bool dfs(int n) {
Java
import java.io.*;import java.util.*;public class cycle {static List<Integer>[] adj;static boolean[] visited, onStack;static List<Integer> cycle;static int N, M;public static void main(String[] args) throws IOException {BufferedReader sc =
Python
import sysMAX_N = 10**5sys.setrecursionlimit(MAX_N)def dfs(node):visited[node], on_stack[node] = True, Truefor u in adj[node]:if on_stack[u]:
Warning!
This code assumes that there are no self-loops.
BFS
The BFS version is known as Kahn's Algorithm.
C++
Course Schedule Solution
#include <iostream>#include <queue>#include <vector>using namespace std;const int MAX_N = 100000;int main() {int n, m;cin >> n >> m;
Java
public class TopoSort {static int[] inDegree;static List<Integer>[] edge; // adjacency liststatic int N; // number of nodesstatic void topological_sort() {Queue<Integer> q = new LinkedList<>();for (int i = 0; i < N; i++) {if (inDegree[i] == 0) { q.add(i); }
Python
from collections import deque# The code is in a function so it can run faster.def main():N, M = map(int, input().split())adj = [[] for _ in range(N)]for _ in range(M):a, b = map(int, input().split())a -= 1b -= 1
Pro Tip
If the length of the array containing the end order does not equal the number of elements that need to be sorted, then there is a cycle in the graph.
Optional
We can also use Kahn's algorithm to extract the lexicographically minimum topological sort by breaking ties lexographically.
Although the above code does not do this, one can simply replace the queue
with a priority_queue
to implement this extension.
Dynamic Programming
Resources | ||||
---|---|---|---|---|
CPH |
One useful property of directed acyclic graphs is, as the name suggests, that no cycles exist. If we consider each node in the graph as a state, we can perform dynamic programming on the graph if we process the states in an order that guarantees for every edge that is processed before . Fortunately, this is the exact definition of a topological sort!
Focus Problem – try your best to solve this problem before continuing!
In this task, we must find the longest path in a DAG.
Solution - Longest Flight Route
Let denote the length of the longest path ending at the node . Clearly
or if is node . If we process the states in topological order, it is guaranteed that will already have been computed before computing .
Note that the implementation of this idea below uses Kahn's algorithm for topological sorting:
C++
#include <bits/stdc++.h>using namespace std;int prev_flight[100000];int dist[100000];int in_degree[100000];vector<int> edge[100000];vector<int> backEdge[100000];
Java
import java.io.*;import java.util.*;// longest_pathpublic class Main {static int[] prevFlight, dist, inDegree;static List<Integer>[] edge;static List<Integer>[] backEdge;static int N, M;
Problems
Status | Source | Problem Name | Difficulty | Tags | |
---|---|---|---|---|---|
CSES | Easy | Show TagsTopoSort | |||
Kattis | Easy | Show TagsTopoSort | |||
Gold | Easy | Show TagsTopoSort | |||
CF | Easy | Show TagsTopoSort | |||
CF | Easy | Show TagsTopoSort | |||
Gold | Normal | Show TagsBinary Search, TopoSort | |||
CF | Hard | Show TagsBitwise, TopoSort | |||
CSES | Hard | Show TagsTopoSort |
Module Progress:
Join the USACO Forum!
Stuck on a problem, or don't understand a module? Join the USACO Forum and get help from other competitive programmers!