Skip to content
advancedPhase 5 · Graphs

Topological Sort

Order vertices in DAGs for scheduling and dependency resolution.

1h
5 problems
Topic Progress0%

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/2solved
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 the primary purpose of Topological Sort?

Question 3 options

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

Question 4 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

What is Topological Sort?

Answer

Topological Sort is a key concept in software engineering.

Question

When to use Topological Sort?

Answer

Use Topological Sort when building production systems that require reliability, scalability, and maintainability.

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