Skip to content
advanced Phase 5 · Graphs

Topological Sort

Order vertices in DAGs for scheduling and dependency resolution.

1h
5 problems
Topic Progress 0%

Topological Sort Concepts

What is Topological Sort?

Topological sort produces a linear ordering of vertices in a Directed Acyclic Graph (DAG) such that for every directed edge u→v, vertex u comes before v in the ordering.

DAG Example:                 Valid Topological Orders:
  0 → 1 → 3                  [0, 1, 2, 3, 4]
  0 → 2 → 3                  [0, 2, 1, 3, 4]
      ↓                       [2, 0, 1, 3, 4]
      4                       (Multiple valid orderings possible)

Key Concepts

  • Prerequisite: Edge u→v means u must come before v
  • DAG: Directed Acyclic Graph (no cycles allowed)
  • In-degree: Number of incoming edges to a vertex
  • Source: Vertex with in-degree 0 (no prerequisites)

When to Use Topological Sort

Problem Type Example
Course prerequisites Take Course 0 before Course 1
Task scheduling Build foundation before walls
Build systems Compile dependencies
Spreadsheet formulas Cell references
Package installation npm/pip dependencies

Two Main Approaches

  1. DFS-based: Use DFS to produce reverse post-order
  2. Kahn's Algorithm (BFS-based): Use in-degree and queue

Both run in O(V + E) time.

Topological Sort Implementations

Kahn's Algorithm (BFS-based)

import java.util.*;

public class TopologicalSort {
    
    // Kahn's Algorithm
    public int[] kahnTopoSort(int n, int[][] edges) {
        // Build graph and compute in-degrees
        Map<Integer, List<Integer>> graph = new HashMap<>();
        int[] inDegree = new int[n];
        
        for (int i = 0; i < n; i++) {
            graph.put(i, new ArrayList<>());
        }
        
        for (int[] edge : edges) {
            graph.get(edge[0]).add(edge[1]);
            inDegree[edge[1]]++;
        }
        
        // Add all sources (in-degree 0) to queue
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            if (inDegree[i] == 0) {
                queue.offer(i);
            }
        }
        
        int[] result = new int[n];
        int index = 0;
        
        while (!queue.isEmpty()) {
            int node = queue.poll();
            result[index++] = node;
            
            for (int neighbor : graph.get(node)) {
                inDegree[neighbor]--;
                if (inDegree[neighbor] == 0) {
                    queue.offer(neighbor);
                }
            }
        }
        
        // Check for cycle
        if (index != n) {
            return new int[]{}; // Cycle exists
        }
        
        return result;
    }
    
    // Returns empty array if cycle exists
    // Returns topological order otherwise
}

DFS-based Topological Sort

public int[] dfsTopoSort(int n, int[][] edges) {
    Map<Integer, List<Integer>> graph = new HashMap<>();
    for (int i = 0; i < n; i++) {
        graph.put(i, new ArrayList<>());
    }
    for (int[] edge : edges) {
        graph.get(edge[0]).add(edge[1]);
    }
    
    int[] color = new int[n]; // 0: unvisited, 1: visiting, 2: visited
    Stack<Integer> stack = new Stack<>();
    
    for (int i = 0; i < n; i++) {
        if (color[i] == 0) {
            if (dfsHelper(graph, i, color, stack)) {
                return new int[]{}; // Cycle detected
            }
        }
    }
    
    int[] result = new int[n];
    for (int i = 0; i < n; i++) {
        result[i] = stack.pop();
    }
    return result;
}

private boolean dfsHelper(Map<Integer, List<Integer>> graph, int node, 
                         int[] color, Stack<Integer> stack) {
    color[node] = 1; // Mark as visiting
    
    for (int neighbor : graph.get(node)) {
        if (color[neighbor] == 1) {
            return true; // Cycle detected
        }
        if (color[neighbor] == 0) {
            if (dfsHelper(graph, neighbor, color, stack)) {
                return true;
            }
        }
    }
    
    color[node] = 2; // Mark as visited
    stack.push(node);
    return false;
}

Course Schedule Problem

// Can finish all courses?
public boolean canFinish(int numCourses, int[][] prerequisites) {
    return kahnTopoSort(numCourses, prerequisites).length > 0;
}

// Return course ordering
public int[] findOrder(int numCourses, int[][] prerequisites) {
    return kahnTopoSort(numCourses, prerequisites);
}

Complexity Comparison

Approach Time Space Detects Cycles
Kahn's (BFS) O(V + E) O(V) Yes (incomplete ordering)
DFS-based O(V + E) O(V) Yes (back edge detection)

Interview Tips

  1. Always check for cycles - if cycle exists, no valid ordering
  2. Kahn's is easier to implement and naturally detects cycles
  3. DFS gives reverse post-order - remember to reverse the result
  4. Multiple valid orderings - any valid order is acceptable
  5. Edge cases: single node, no edges, all edges in one direction

Practice Problems

0 / 2 solved
Course Schedule
Topological Sort - Cycle Detection

There are a total of numCourses courses labeled 0 to numCourses-1. Some courses have prerequisites. Given prerequisites[i] = [ai, bi], you must take course bi before ai. Return true if you can finish all courses.

Example:

Input: numCourses = 2, prerequisites = [[1,0]]

Output: true

Take course 0 first, then course 1.

Optimal Solution — O(V + E) time, O(V + E) space

Kahn's algorithm: if result contains all courses, return true

class Solution {
    public boolean canFinish(int numCourses, int[][] prerequisites) {
        List<List<Integer>> graph = new ArrayList<>();
        int[] inDegree = new int[numCourses];
        for (int i = 0; i < numCourses; i++) graph.add(new ArrayList<>());
        for (int[] pre : prerequisites) {
            graph.get(pre[1]).add(pre[0]);
            inDegree[pre[0]]++;
        }
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < numCourses; i++)
            if (inDegree[i] == 0) queue.offer(i);
        int count = 0;
        while (!queue.isEmpty()) {
            int course = queue.poll();
            count++;
            for (int next : graph.get(course)) {
                if (--inDegree[next] == 0) queue.offer(next);
            }
        }
        return count == numCourses;
    }
}

Edge Cases:

  • No prerequisites: return true
  • Single course: return true
  • Cycle exists: return false
Course Schedule II
Topological Sort

Return the ordering of courses to finish all courses. If impossible, return empty array.

Example:

Input: numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]

Output: 0123

One valid ordering: 0 → 1 → 2 → 3.

Optimal Solution — O(V + E) time, O(V + E) space

Kahn's algorithm: store result order

class Solution {
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        List<List<Integer>> graph = new ArrayList<>();
        int[] inDegree = new int[numCourses];
        for (int i = 0; i < numCourses; i++) graph.add(new ArrayList<>());
        for (int[] pre : prerequisites) {
            graph.get(pre[1]).add(pre[0]);
            inDegree[pre[0]]++;
        }
        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < numCourses; i++)
            if (inDegree[i] == 0) queue.offer(i);
        int[] order = new int[numCourses];
        int idx = 0;
        while (!queue.isEmpty()) {
            int course = queue.poll();
            order[idx++] = course;
            for (int next : graph.get(course))
                if (--inDegree[next] == 0) queue.offer(next);
        }
        return idx == numCourses ? order : new int[0];
    }
}

Edge Cases:

  • No prerequisites
  • Cycle exists: return empty
  • Multiple valid orders

Quiz

1. Can topological sort be applied to a graph with cycles?

Question 1 options

2. In Kahn's algorithm, what does it mean if we can't add all vertices to the result?

Question 2 options

3. What is a common mistake when implementing Topological Sort?

Question 3 options

Flashcards

Question

What is topological sort and when can it be used?

Answer

Topological sort produces a linear ordering of vertices in a DAG where for every edge u→v, u comes before v. It's used for dependency resolution, task scheduling, and course prerequisites.

Question

How does Kahn's algorithm detect cycles?

Answer

Kahn's algorithm adds vertices with in-degree 0 to a queue. If after processing, not all vertices are in the result, the remaining vertices form a cycle (they always have non-zero in-degree).

Question

Topological Sort best practices

Answer

Follow SOLID principles, write clean code, test thoroughly, document decisions, and monitor in production.

Revision Notes

Key Takeaways

  • 1. Topological sort only works on DAGs (Directed Acyclic Graphs)
  • 2. Kahn's algorithm uses in-degree tracking and BFS
  • 3. DFS-based uses reverse post-order detection
  • 4. Cycle detection: Kahn's returns incomplete result, DFS finds back edge
  • 5. Multiple valid orderings may exist - any valid one is acceptable

Interview Tips

  • Course Schedule problems are classic topological sort applications
  • Clarify the edge direction: [a,b] means b→a (b before a)
  • Kahn's is easier to implement and debug than DFS-based
  • Always check for cycles - if cycle, return empty/impossible
  • For finding ALL valid orderings, use DFS with backtracking

Cheat Sheet

Topological Sort Cheat Sheet

When to Use

  • Dependency resolution (courses, build systems)
  • Task ordering with prerequisites
  • Detecting cycles in directed graphs

Kahn's Algorithm (BFS)

int[] inDegree = new int[n];
// Build graph, compute in-degrees
Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < n; i++) {
    if (inDegree[i] == 0) queue.offer(i);
}
int[] result = new int[n];
int idx = 0;
while (!queue.isEmpty()) {
    int node = queue.poll();
    result[idx++] = node;
    for (int neighbor : graph.get(node)) {
        if (--inDegree[neighbor] == 0) queue.offer(neighbor);
    }
}
// If idx != n, cycle exists

DFS-based Topological Sort

// Reverse post-order gives topological sort
void dfs(int node) {
    color[node] = 1;
    for (int neighbor : graph.get(node)) {
        if (color[neighbor] == 1) return true; // Cycle!
        if (color[neighbor] == 0 && dfs(neighbor)) return true;
    }
    color[node] = 2;
    stack.push(node);
}

Cycle Detection

  • Kahn's: If result doesn't contain all vertices → cycle
  • DFS: If back edge found (node in visiting state) → cycle

Complexity

  • Time: O(V + E)
  • Space: O(V)

Course Schedule Pattern

  • prerequisites[i] = [a, b] means b → a (b before a)
  • FindOrder: Kahn's algorithm on prerequisite graph
  • CanFinish: Check if topological sort includes all courses