Skip to content
Problem · Canonical Warm-Up
mediumtopological-sort · cycle-detection · bfs · dfsTime · O(V + E)Space · O(V + E)

Course Schedule

Problem statement

Statement

There are numCourses courses labelled 0 to numCourses - 1. You are given prerequisites, where [a, b] means course b must be taken before course a. Return true if every course can be completed, and false otherwise.

Examples

numCourses = 2, prerequisites = [[1, 0]]
-> true              (take 0, then 1)

numCourses = 2, prerequisites = [[1, 0], [0, 1]]
-> false             (each needs the other first)

numCourses = 3, prerequisites = []
-> true              (no constraints at all)

numCourses = 4, prerequisites = [[1, 0], [2, 1], [3, 2], [1, 3]]
-> false             (1 -> 2 -> 3 -> 1 is a cycle; course 0 is fine
                      but the question asks about all courses)

Constraints

  • 1 <= numCourses <= 2000
  • 0 <= len(prerequisites) <= 5000
  • No duplicate edges. Self-loops are possible in principle and are immediately disqualifying.
  • The graph may be disconnected, so an isolated course with no prerequisites is still a course.

What this problem is really testing

Translation. The prose is about courses; the question is whether a directed graph contains a cycle. Candidates who spot that in the first thirty seconds have effectively finished, and candidates who start reasoning about courses directly tend to write something that works on the examples and fails on a cycle that does not include course 0.

Once translated, the claim to defend is that the courses are completable if and only if the graph is acyclic. The forward direction is easy: a valid completion order lists every course after its prerequisites, and a cycle would require each of its members to precede itself. The reverse direction is the interesting one and it is constructive: in any finite directed acyclic graph there is always at least one vertex with no incoming edges, because following edges backwards from any vertex must terminate or repeat, and repeating means a cycle. Take that vertex, remove it, and the remainder is still acyclic. Repeat until the graph is empty, and the order in which you removed vertices is a valid schedule. That argument is Kahn's algorithm, and having it in hand means you are not reciting a procedure but explaining why it terminates correctly.

The framing worth carrying away is that of a dependency queue in any build system. A package can be compiled once everything it imports is compiled, so the build starts with leaf packages and works inward, and a circular import means the build simply cannot start. Course scheduling, task scheduling, spreadsheet recalculation and module linking are all the same graph question wearing different vocabulary, which is why topological sort pays for itself so many times over.