Problem Walkthrough

Course Schedule II LeetCode 210 Guide

Return the actual topological ordering of courses using Kahn's BFS — not just whether it's possible, but the exact sequence in which to take all n courses given their prerequisites.

8 min read|

Course Schedule II

LeetCode 210

Problem Overview — Return the Course Ordering

LeetCode 210 Course Schedule II gives you n courses labeled 0 to n-1 and a list of prerequisite pairs [a, b] meaning you must take course b before course a. Your task is not just to check whether all courses can be finished, but to return one valid ordering in which to take them all. If no valid ordering exists (a cycle is present), return an empty array.

This is a classic topological sort problem. A topological ordering of a directed graph is a linear arrangement of nodes such that for every directed edge u → v, node u appears before v in the sequence. Topological sort is only possible on directed acyclic graphs (DAGs) — the presence of a cycle means no such ordering can exist.

Understanding the output contract is important: any valid topological ordering is accepted. There may be multiple correct answers. You are not asked for the lexicographically smallest ordering or any specific one — just a valid sequence that respects all prerequisite constraints.

  • n courses labeled 0 to n-1, 0 <= n <= 2000
  • prerequisites[i] = [a, b]: must take b before a
  • Return any valid ordering of all n courses
  • Return [] if it is impossible to finish all courses (cycle exists)
  • Any valid topological ordering is accepted as output

From Course Schedule I to II

Course Schedule I (LeetCode 207) only asks whether you can finish all courses — a boolean feasibility check. Course Schedule II asks for the actual ordering. The good news: if you already understand Kahn's BFS solution for Course Schedule I, you are 90% of the way there. The only change is collecting the nodes as you dequeue them.

In Course Schedule I, you process each zero in-degree node, decrement its neighbors' in-degrees, and enqueue any that reach zero. You finish by checking whether the total processed count equals n. In Course Schedule II, every time you dequeue a node and process it, you append it to a result list. That result list is your topological order.

This tight relationship between the two problems is why they are often studied together. Mastering Kahn's algorithm with the order-collection extension gives you a template that handles both problems with minimal code change.

💡

Course Schedule I → II Is Free

If you can solve Course Schedule I with Kahn's BFS, Course Schedule II costs one extra line: append each dequeued node to a result list. At the end, if len(result) == n, return result — otherwise return []. The cycle detection is identical.

Kahn's BFS for Ordering

Kahn's algorithm works by repeatedly removing nodes with no incoming edges (in-degree zero) from the graph. These nodes have no prerequisites that haven't been satisfied yet, so they are safe to schedule next. After removing a node, you update the in-degrees of its neighbors, and any that reach zero become newly schedulable.

The BFS formulation uses a queue to track all currently zero in-degree nodes. You initialize the queue with every node that starts at in-degree zero. Then you process nodes one at a time: dequeue a node, add it to the result list, and for each of its outgoing neighbors, decrement their in-degree and enqueue them if they hit zero.

The process continues until the queue is empty. At that point, either every node has been processed and the result list contains a valid topological ordering, or some nodes were never enqueued because they remained in a cycle with persistent in-degrees above zero.

  1. 1Build adjacency list and compute in-degree array for all n nodes
  2. 2Initialize deque with all nodes whose in-degree is 0
  3. 3While queue is not empty: dequeue node, append to result list
  4. 4For each neighbor of dequeued node: decrement neighbor's in-degree
  5. 5If neighbor in-degree reaches 0, enqueue it
  6. 6After BFS: if len(result) == n return result, else return []

Cycle Detection — When to Return Empty Array

After Kahn's BFS completes, the cycle detection check is a single comparison: if the result list contains exactly n nodes, you processed every course and the result is a valid ordering. If the result list contains fewer than n nodes, some courses were never reachable because they were trapped in a cycle.

Nodes in a cycle always have in-degree greater than or equal to 1 even after all other nodes have been processed. Because the only way to enter the queue is to have in-degree zero, cycle members are permanently blocked. They never get dequeued, never get appended to the result, and the result length falls short of n.

This implicit cycle detection is one of the key advantages of Kahn's algorithm over DFS-based topological sort. There is no need to maintain an explicit visited / in-progress / done state. The in-degree arithmetic handles everything: if a node's in-degree never reaches zero, it is part of a cycle.

ℹ️

Kahn's Detects Cycles Implicitly

Nodes in a cycle always have in-degree >= 1 and never enter the queue. After BFS completes, check len(result) == n. If true, no cycle — return result. If false, a cycle blocked some nodes — return []. No explicit cycle-tracking state needed.

DFS Alternative — Post-Order Reversal

The DFS approach to topological sort uses three node states: unvisited (0), in-progress (1), and done (2). For each unvisited node, you run DFS. If you encounter an in-progress node during DFS, you have found a cycle. When DFS finishes a node (all its descendants are done), you append it to a result list.

After all DFS calls complete without detecting a cycle, you reverse the result list. The reversal is necessary because nodes are appended in post-order — a node is added after all its descendants. Reversing converts this to pre-order, which is the correct topological sequence (prerequisites before dependents).

Kahn's BFS is generally preferred for this problem because it naturally produces the order without a reversal step and the cycle check is a single length comparison. DFS is equally correct and also O(V+E), but requires careful state management with the three-color marking to avoid false cycle detections.

  1. 1Initialize state array: 0=unvisited, 1=in-progress, 2=done
  2. 2For each unvisited node, call DFS(node)
  3. 3DFS: mark node in-progress (1), recurse on all neighbors
  4. 4If neighbor is in-progress (1): cycle detected, return False
  5. 5After all neighbors processed: mark node done (2), append to result
  6. 6After all DFS calls: reverse result list and return

Code Walkthrough — Python and Java

In Python, build an adjacency list and in-degree array with a single loop over prerequisites. Initialize a deque with all nodes at in-degree 0. Run the BFS loop: pop left, append to result, decrement neighbors, enqueue zeros. After the loop, return result if len(result) == numCourses else []. The full solution is about 15 lines.

In Java, use an ArrayList<List<Integer>> for the adjacency list and an int[] indegree array. Use ArrayDeque<Integer> for the BFS queue. The logic is identical to Python. The result is an int[] that you fill index by index as you dequeue nodes. Return the result array if the fill count equals numCourses, otherwise return new int[]{}.

Both implementations run in O(V+E) time where V = numCourses and E = prerequisites.length. Space complexity is also O(V+E) for the adjacency list plus O(V) for the in-degree array and queue. The space is dominated by the adjacency list representation of the graph.

⚠️

Order Is Not Unique

The topological ordering produced by BFS depends on the order nodes enter the queue. Any valid ordering is accepted by LeetCode, but if your queue processes nodes in arbitrary order, the output may differ from run to run on inputs with multiple valid orderings. This is expected and correct behavior.

Ready to master algorithm patterns?

YeetCode flashcards help you build pattern recognition through active recall and spaced repetition.

Start practicing now