Course Schedule — Coding Interview Walkthrough
You’re in a coding interview. The interviewer says: “You have a list of courses and their prerequisites. Can you finish all of them?” You know it’s a graph problem. You know you need cycle detection. But the moment you start coding, they’re watching whether you get the edge direction right, whether you reach for two states or three, and whether you can explain why a visiting state matters when they ask the follow-up about Course Schedule II.
This walkthrough breaks down exactly how to structure your approach, what to say out loud, and where candidates lose points even when they get the right answer.
The Problem
You are given numCourses courses labeled 0 to numCourses - 1, and a list of prerequisite pairs. Each pair [a, b] means you must take course b before you can take course a. Return true if it’s possible to finish all courses, or false if it’s not.
A situation where it’s not possible would be if two courses require each other, a circular dependency that makes it impossible to ever start.
Example
Input
numCourses = 2, prerequisites = [[1, 0]]
Output
true
Here, course 1 requires course 0. You can take 0 first, then 1. There’s no cycle, so it’s possible to complete everything.
Input
numCourses = 2, prerequisites = [[1, 0], [0, 1]]
Output
false
Now course 1 requires 0 and course 0 requires 1. They’re mutually dependent, so you return false. (LeetCode #207)
Already comfortable with the solution? Practice it in a mock interview ->
What Interviewers Are Actually Testing
This problem isn’t really about scheduling. It’s a cycle detection problem dressed up in a practical scenario.
| Skill | What Interviewers Observe |
|---|---|
| Graph modeling | Can you represent prerequisites as a directed graph (adjacency list)? |
| Cycle detection intuition | Do you recognize this as a topological sort / DFS cycle detection problem? |
| State tracking | Do you understand why you need three states (unvisited, visiting, visited)? |
| Edge case handling | Do you handle disconnected graphs, courses with no prerequisites, empty input? |
| Algorithm choice | Can you articulate the tradeoffs between DFS and Kahn’s BFS algorithm? |
| Communication | Do you explain your approach before jumping into code? |
Step 1: Clarifying Questions
Before writing any code, pause and ask these questions:
-
“Can there be courses with no prerequisites at all?” Yes, and these are trivially completable. Your traversal must cover all
numCoursesnodes, not just those mentioned in the prerequisite list. -
“Can the prerequisites list be empty?” If
prerequisites = [], the answer is alwaystrue. Confirming this lets you handle it as a clean base case. -
“Are there duplicate prerequisite pairs?” LeetCode guarantees no duplicates, but asking shows you’re thinking about input hygiene.
-
“Is the graph guaranteed to be connected?” No, and this is critical. If you only traverse from one starting node, you may miss entire disconnected components.
-
“Should I return the valid ordering, or just whether one exists?” This problem only asks for a boolean. But asking shows you’re aware of Course Schedule II, which is a natural follow-up.
Step 2: A First (Non-Optimal) Idea
A candidate’s first instinct is often to simulate the process: find a course with no prerequisites, “take” it, remove it from the dependency list, and repeat.
def canFinish(numCourses, prerequisites):
in_degree = [0] * numCourses
for _, pre in prerequisites:
in_degree[pre] += 1 # This logic is already wrong — tracking wrong direction
completed = 0
changed = True
while changed:
changed = False
for i in range(numCourses):
if in_degree[i] == 0:
in_degree[i] = -1 # Mark as "taken"
completed += 1
changed = True
# ... update dependents (complex to implement correctly)
return completed == numCourses
This approach is conceptually valid but hard to implement cleanly. The inner loop rescans all courses on every pass, giving O(V^2) time complexity. It’s also error-prone: candidates frequently get the dependency direction backwards.
More importantly, it doesn’t translate into a recognizable algorithm. Interviewers want to see you reach for DFS or topological sort, established patterns with clear correctness arguments.
Step 3: The Key Insight
The “aha” moment: “can we finish all courses?” is exactly equivalent to “does this directed graph contain a cycle?” If there’s a cycle, no valid ordering exists. If there’s no cycle, a topological order always exists.
The elegant way to detect cycles with DFS uses three node states:
- 0 (Unvisited): We haven’t explored this node yet.
- 1 (Visiting): We’re currently in a DFS path that includes this node.
- 2 (Visited): We’ve fully explored this node and confirmed it has no cycles.
If during DFS we encounter a node already in state 1 (currently on our path), we’ve found a back edge, which means a cycle. If we finish exploring a node with no cycle, we mark it state 2 so future DFS calls skip it entirely.
Here’s how the three states evolve during DFS, both when there’s no cycle and when a cycle gets caught:
Step 4: Optimal Strategy (DFS Three-State Cycle Detection)
- Build an adjacency list: For each pair
[a, b], addbto the neighbor list ofa(meaning “to takea, you needb”). - Initialize a state array of size
numCourses, all set to0(unvisited). - Define
has_cycle(node):- If
state[node] == 1: returnTrue(back edge found, cycle). - If
state[node] == 2: returnFalse(already verified, skip). - Set
state[node] = 1(currently visiting). - Recurse into all neighbors. If any returns
True, propagate it. - Set
state[node] = 2(fully visited, no cycle from here). - Return
False.
- If
- Iterate over all nodes
0tonumCourses - 1. Callhas_cycle(i)for each unvisited node. - Return
Trueif no cycle was found across any component.
Python Implementation
from typing import List
from collections import defaultdict
class Solution:
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
# Build adjacency list: course -> list of prerequisites
graph = defaultdict(list)
for course, prereq in prerequisites:
graph[course].append(prereq)
# 0 = unvisited, 1 = visiting (in current DFS path), 2 = visited (safe)
state = [0] * numCourses
def has_cycle(node: int) -> bool:
if state[node] == 1:
return True # Back edge detected — cycle found
if state[node] == 2:
return False # Already confirmed safe — skip
state[node] = 1 # Mark as currently being explored
for neighbor in graph[node]:
if has_cycle(neighbor):
return True # Propagate cycle detection upward
state[node] = 2 # Mark as fully explored — no cycle from here
return False
# Check every node — graph may be disconnected
for course in range(numCourses):
if state[course] == 0:
if has_cycle(course):
return False
return True
The defaultdict(list) handles courses with no prerequisites cleanly. The explicit state array is cleaner than using a visited set because it distinguishes the three states needed for cycle detection.
Time Complexity
| Operation | Complexity |
|---|---|
| Building the adjacency list | O(E), one pass over prerequisites |
| DFS traversal (each node visited once) | O(V) |
| Processing all edges across DFS | O(E) |
| Overall Time | O(V + E) |
| Overall Space | O(V + E), graph storage + recursion stack |
Here, V = numCourses and E = len(prerequisites). Each node is visited at most once (the state == 2 check prevents re-processing), and each edge is traversed at most once.
Common Interview Mistakes
-
Using only two states instead of three Many candidates use a simple
visitedboolean. This can’t distinguish “currently on the DFS path” from “already fully processed,” causing false cycle detections or missing real ones. You need thevisitingstate. -
Getting the edge direction backwards If the problem says
[a, b]means “a requires b,” some candidates addb -> ainstead ofa -> b. Draw the graph explicitly before coding. -
Only traversing from node 0 If you start DFS only from
course 0, you’ll miss disconnected components. Always iterate over all nodes. -
Forgetting courses with no prerequisites Courses not in
prerequisitesstill exist in the graph. They just have no outgoing edges. Your code must not crash whengraph[node]is empty. -
Coding immediately without drawing the graph On a whiteboard, sketch the graph for the examples before writing code. This catches edge-direction mistakes early.
-
Not explaining the three-state logic verbally If you write the
statearray without explaining why three values are needed, the interviewer may assume you memorized the solution. -
Confusing this with undirected graph cycle detection Undirected cycle detection only needs two states. Directed graphs require three. If you conflate the two, you’ll give an incorrect solution.
What a Strong Candidate Sounds Like
“This is a cycle detection problem on a directed graph. I’ll model the prerequisites as an adjacency list, then run DFS across all nodes. The key is tracking three states: unvisited, currently-visiting, and fully-visited. If I hit a node that’s currently on my active DFS path, I’ve found a back edge, which means a cycle. If I finish exploring a node without finding a cycle, I mark it fully-visited so I never process it again. I need to run this from every node since the graph might be disconnected. Time complexity is O(V + E).”
Example Interview Dialogue
Interviewer: “You have numCourses courses and a list of prerequisite pairs. Can you finish all courses?”
Candidate: “Before I start: a pair [a, b] means I must take b before a, right? And should I return just true/false, or the actual order?”
Interviewer: “Correct on the direction. Just the boolean.”
Candidate: “Got it. So this is asking whether the directed graph has a cycle. I’ll build an adjacency list and do DFS with three states to detect back edges. I need to run DFS from every node since the graph may not be connected.”
[Draws example graph, then writes solution]
Interviewer: “What if I asked you to return the actual order in which to take the courses?”
Candidate: “That’s Course Schedule II, topological sort. I’d collect nodes into a result list as I mark them fully-visited. The order I add them gives a valid topological ordering. Alternatively, I could use Kahn’s algorithm with in-degree tracking and a queue, that’s BFS-based and also O(V + E).”
Interviewer: “Could you hit a recursion limit on large inputs?”
Candidate: “Yes. If the graph is a long chain with 10^5 nodes, Python’s default recursion depth of ~1000 would break. I’d either increase the limit with sys.setrecursionlimit, or convert the DFS to an iterative approach using an explicit stack.”
Related Problems to Practice
-
Course Schedule II (LeetCode 210). The direct follow-up: return the actual course order. Requires storing the topological sort result.
-
Alien Dictionary (LeetCode 269). Derive a topological ordering from character relationships. Same DFS cycle detection with more complex graph construction.
-
Longest Increasing Path in a Matrix (LeetCode 329). DFS with memoization on a DAG. Deepens understanding of state caching in graph traversal.
-
Sequence Reconstruction (LeetCode 444). Verify that a topological order is unique. Same directed graph model applied to a uniqueness problem.
-
Sort Items by Groups Respecting Dependencies (LeetCode 1203). A harder topological sort problem with two levels of dependency.
From the Intervu walkthrough collection:
- Number of Islands — BFS/DFS on implicit graphs.
- Clone Graph — Graph traversal with state tracking.
- Minimum Height Trees — Topological peeling on undirected graphs.
Practice This in a Mock Interview
Reading a walkthrough and solving a problem under interview pressure are very different skills. You won’t have access to notes, and you’ll need to model the graph from scratch, recall the three-state DFS trick without hints, and handle follow-up questions about topological sort variants.
Start a mock interview for Course Schedule on Intervu.
Further reading:
- How to Prepare for a Coding Interview in 2026, the complete roadmap
- Why LeetCode alone isn’t enough, and what to practice instead
- Practice any LeetCode problem as a live mock interview
- The Grind 75 Pathway, a structured study plan with AI practice links
- Browse all walkthroughs on GitHub, condensed solutions with code
- Data Structures for Coding Interviews, including graphs and DFS patterns