🎉 All 30 days are live — the full DSA-30 course, from Big-O to System Design. See the roadmap →

Course Schedule medium

Description

There are numCourses courses labeled 0 to numCourses - 1. You’re given a list prerequisites where prerequisites[i] = [a, b] means you must take course b before course a.

Return true if you can finish all courses, false otherwise.

Examples

> Case 1:
    Input:  numCourses = 2, prerequisites = [[1, 0]]
    Output: true       # Take 0, then take 1.
 
> Case 2:
    Input:  numCourses = 2, prerequisites = [[1, 0], [0, 1]]
    Output: false      # Cycle: 0 needs 1, 1 needs 0. Impossible.
 
> Case 3:
    Input:  numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]
    Output: true       # 0 → 1 → 3 and 0 → 2 → 3. A valid order: 0,1,2,3.

Constraints

  • 1 <= numCourses <= 2000
  • 0 <= prerequisites.length <= 5000

Restating the problem

The prerequisites form a directed graph: edge b → a means “b must be taken before a.” The question becomes:

Does this directed graph have a cycle?

If yes → no valid ordering exists → return false. If no → it’s a DAG → a topological order exists → return true.

So this is cycle detection in a directed graph. Two classic approaches:

  1. DFS with three-color marking. Track each node as unvisited / on-current-stack / fully-done. A back-edge to an “on-current-stack” node means a cycle.
  2. BFS / Kahn’s algorithm. Repeatedly remove nodes with in-degree 0. If you can remove all n nodes, the graph is a DAG.

Both are O(V + E). Pick the one you find easier to write under pressure.

Try it yourself

Course Schedule — return whether all courses can be finished
Hint
prerequisites form a directed graph (edge b → a). You can finish all courses iff that graph has no cycle. Track each node as unvisited / on-stack / done — seeing an on-stack node again is a cycle.
Reveal solution

Approach 1: DFS with three colors

The three states for each node:

  • 0 (white) — unvisited
  • 1 (gray) — currently being visited (on the recursion stack)
  • 2 (black) — fully visited (we’ve already explored everything reachable)

A cycle is found when DFS sees a gray node as a neighbour — that means we’ve looped back to something still on the call stack.

⚠️

The classic bug: using a single boolean visited array. That can’t distinguish “currently being explored” from “fully done” — and you’ll either miss cycles or claim cycles where there aren’t any. Three states are needed.

Approach 2: Kahn’s algorithm (BFS-based topological sort)

The idea: every DAG has at least one node with in-degree 0 — a node with no prerequisites. Remove it (and the edges going out of it), then look for another. Repeat until either the graph is empty (DAG, return true) or you’re stuck with nodes that still have prerequisites (cycle, return false).

from collections import deque
 
def can_finish(numCourses, prerequisites):
    in_degree = [0] * numCourses
    adj = [[] for _ in range(numCourses)]
    for a, b in prerequisites:
        adj[b].append(a)
        in_degree[a] += 1
 
    # Start with all zero-in-degree nodes
    queue = deque(i for i in range(numCourses) if in_degree[i] == 0)
    processed = 0
    while queue:
        u = queue.popleft()
        processed += 1
        for v in adj[u]:
            in_degree[v] -= 1
            if in_degree[v] == 0:
                queue.append(v)
 
    return processed == numCourses

If we processed every course, all had a valid place in the ordering — no cycle. If some courses are stuck with positive in-degree, those form the cycle.

Bonus: Kahn’s algorithm doesn’t just detect cycles — it also produces a valid topological order in [u for u in dequeue_order]. Save those u values to a list and you’ve solved Course Schedule II at the same time.

Comparison

AspectDFS 3-colorKahn’s BFS
Mental model”Is there a back-edge?""Can I peel off in-degree-0 nodes?”
Produces order?Yes, in reverse post-orderYes, directly
Recursion?YesNo (iterative)
ComplexityO(V + E)O(V + E)
Personal preferenceCleaner code if you’re comfy with recursionCleaner when you also need the order

Both are interview-correct answers; mention which you’d choose and why.

Analysis

  • Time: O(V + E) for both.
  • Space: O(V + E) for the adjacency list, plus O(V) for the visited / in-degree array.
Finished this page?