Course Schedule
Problem
Given the total number of courses and a list of prerequisite pairs, determine if it is possible to finish all courses by taking them in an order that satisfies all prerequisites.
- 1 ≤ numCourses ≤ 10⁵
- 0 ≤ prerequisites.length ≤ 5000
- prerequisites[i].length == 2
- 0 ≤ a_i, b_i < numCourses
- All the pairs prerequisites[i] are unique.
Example
numCourses = 2, prerequisites = [[1,0]]trueThere are two courses: 0 and 1. To take course 1, you must first take course 0. The order [0,1] satisfies the prerequisites, so it is possible to finish all courses.
Approach
Straightforward Solution
A brute-force approach might try all permutations of courses to check if prerequisites are satisfied, which is factorial in complexity and infeasible for large inputs.
Core Observation
The problem reduces to detecting if a directed graph (courses as nodes, prerequisites as edges) contains a cycle. If a cycle exists, it is impossible to complete all courses because some courses depend on each other circularly.
Path to Optimal
PreviewRecognizing the problem as cycle detection in a directed graph leads to using DFS with a recursion stack (visit set) to detect back edges indicating cycles. Constructing an adjacency list from prerequisites allows efficient traversal…
Full step-by-step walkthrough on Pro →
Optimal Approach
PreviewBuild an adjacency list mapping each course to its prerequisites. For each course, perform a DFS to check for cycles by tracking nodes in the current recursion stack…
Full step-by-step walkthrough on Pro →
Want the full reasoning chain?
Unlock the complete walkthrough, line-by-line analysis, and recall drill.
Unlock ProTime
O(V + E)
Each course (vertex) is visited once, and each prerequisite (edge) is explored once during DFS. Memoization by clearing prerequisites ensures no repeated work, resulting in linear time relative to the graph size.
Space
O(V + E)
The adjacency list stores all edges, requiring O(E) space. The recursion stack and visit set use O(V) space in the worst case, where V is the number of courses.
Pattern Spotlight
DFS (Cycle Detection in Directed Graph)
Use a recursion stack (visit set) to detect cycles by tracking nodes currently in the path; revisiting a node in this stack signals a cycle, enabling early termination.
Solution
| 1 | class Solution:
|
| 2 | def canFinish(self, numCourses: int, prerequisites: list[list[int]]) -> bool:
|
| 3 | pre_map = {i: [] for i in range(numCourses)}
|
| 4 | for crs, pre in prerequisites:
|
| 5 | pre_map[crs].append(pre)
|
| 6 |
|
| 7 | visit_set = set()
|
| 8 | def dfs(crs):
|
| 9 | if crs in visit_set:
|
| 10 | return False
|
| 11 | if pre_map[crs] == []:
|
| 12 | return True
|
| 13 |
|
| 14 | visit_set.add(crs)
|
| 15 | for pre in pre_map[crs]:
|
| 16 | if not dfs(pre): return False
|
| 17 | visit_set.remove(crs)
|
| 18 | pre_map[crs] = []
|
| 19 | return True
|
| 20 |
|
| 21 | for crs in range(numCourses):
|
| 22 | if not dfs(crs): return False
|
| 23 | return True |
Step-by-Step Solution
Build Adjacency List Mapping Courses to Prerequisites
| 3 | pre_map = {i: [] for i in range(numCourses)}
|
| 4 | for crs, pre in prerequisites:
|
| 5 | pre_map[crs].append(pre)
|
Objective
To represent the course prerequisites as a graph for efficient traversal.
Key Insight
An adjacency list is a natural and efficient way to represent directed graphs, especially when edges are sparse. Mapping each course to its list of prerequisites allows quick access to dependencies during DFS. This structure transforms the problem into graph traversal and cycle detection.
Interview Quick-Check
Core Logic
The adjacency list stores all prerequisite edges, enabling O(1) access to a course's prerequisites during DFS.
Common Pitfalls & Bugs
Forgetting to initialize all courses in the adjacency list can cause key errors or missed nodes during traversal.
Use DFS with Recursion Stack to Detect Cycles
To recursively explore prerequisites and detect cycles by tracking the current path.
Iterate Over All Courses to Verify Completion Feasibility
To ensure all courses are checked for cycles and dependencies.
2 more steps with full analysis available on Pro.
Line Analysis
This solution has 9 Critical lines interviewers watch for.
if crs in visit_set:
Check if the current course is already in the recursion stack.
Detecting a course in the current path signals a cycle, which invalidates the course completion order.
visit_set.remove(crs)
Remove the current course from the recursion stack after exploration.
Removing the course restores state for other DFS paths and prevents false cycle detection in separate branches.
visit_set = set()
Initialize a set to track courses currently in the recursion stack.
The visit_set tracks the current DFS path to detect cycles by identifying back edges, which is critical for correctness.
Full line-by-line criticality + rationale for all 18 lines available on Pro.
Test Your Understanding
Why does detecting a node already in the recursion stack during DFS indicate a cycle?
See the answer with Pro.
Related Problems
DFS pattern
Don't just read it. Drill it.
Reconstruct Course Schedule from memory until it sticks. AlgoDrill blanks out key lines and makes you fill them back in, step by step.
Unlock the Course Schedule drill