207. Course Schedule
mediumAsked at GustoDetermine whether all courses can be finished given prerequisite constraints — which is a cycle detection problem on a directed graph. Gusto asks this to test topological sort and graph reasoning in a domain they understand well: dependency graphs in their payroll and benefits configuration.
By Alex Chen, Founder, InterviewChamp.AI · Last verified
Source citations
Public interview reports confirming this problem appears in Gusto loops.
- Glassdoor (2025-12)— Cited in Gusto SWE onsite reports as a graph medium that tests cycle detection and topological ordering.
- Blind (2025-09)— Gusto threads list Course Schedule as a medium-hard problem that surfaces in senior engineering rounds.
Problem
There are numCourses courses labeled 0 to numCourses−1. You are given an array prerequisites where prerequisites[i] = [ai, bi] indicates that you must take course bi first if you want to take course ai. Return true if you can finish all courses. Return false if it is impossible (i.e., there is a cycle).
Constraints
1 <= numCourses <= 20000 <= prerequisites.length <= 5000prerequisites[i].length == 20 <= ai, bi < numCoursesAll the pairs prerequisites[i] are unique.
Examples
Example 1
numCourses = 2, prerequisites = [[1,0]]trueExplanation: Take course 0 then course 1 — no cycle.
Example 2
numCourses = 2, prerequisites = [[1,0],[0,1]]falseExplanation: Course 0 requires 1 and course 1 requires 0 — a cycle, impossible.
Approaches
1. DFS cycle detection (3-colour marking)
Build adjacency list. DFS from each unvisited node. Mark nodes as 'in-progress' (grey) while exploring and 'done' (black) when finished. A back edge to a grey node means a cycle.
- Time
- O(V + E)
- Space
- O(V + E)
function canFinish(numCourses, prerequisites) {
const adj = Array.from({ length: numCourses }, () => []);
for (const [course, prereq] of prerequisites) adj[prereq].push(course);
// 0 = unvisited, 1 = in-progress, 2 = done
const state = new Array(numCourses).fill(0);
function hasCycle(node) {
if (state[node] === 1) return true; // back edge → cycle
if (state[node] === 2) return false; // already fully explored
state[node] = 1; // mark in-progress
for (const neighbor of adj[node]) {
if (hasCycle(neighbor)) return true;
}
state[node] = 2; // mark done
return false;
}
for (let i = 0; i < numCourses; i++) {
if (hasCycle(i)) return false;
}
return true;
}Tradeoff: O(V+E) time and space. The 3-colour (unvisited/in-progress/done) pattern is the canonical DFS cycle-detection approach. Explicit and easy to reason about.
2. Topological sort via Kahn's algorithm (BFS)
Compute in-degrees. Start with all zero-in-degree nodes. Remove them, decrement neighbours. If all nodes are processed, no cycle exists.
- Time
- O(V + E)
- Space
- O(V + E)
function canFinish(numCourses, prerequisites) {
const inDegree = new Array(numCourses).fill(0);
const adj = Array.from({ length: numCourses }, () => []);
for (const [course, prereq] of prerequisites) {
adj[prereq].push(course);
inDegree[course]++;
}
const queue = [];
for (let i = 0; i < numCourses; i++) {
if (inDegree[i] === 0) queue.push(i);
}
let processedCount = 0;
while (queue.length) {
const node = queue.shift();
processedCount++;
for (const neighbor of adj[node]) {
if (--inDegree[neighbor] === 0) queue.push(neighbor);
}
}
return processedCount === numCourses;
}Tradeoff: Also O(V+E). Iterative — no recursion stack overhead. Returns true iff all courses are processed, which is equivalent to the graph being acyclic. Preferred when recursion depth could be an issue.
Gusto-specific tips
Frame it as graph problems first: 'This is cycle detection on a directed graph. If there's a cycle, it's impossible to complete all courses.' Gusto interviewers like candidates who name both approaches (DFS 3-colour and Kahn's BFS) and explain the tradeoffs before choosing one. If they ask about the follow-up Course Schedule II (return the ordering), note that Kahn's naturally gives the topological order.
Common mistakes
- Using a single boolean visited array instead of 3 states — can't distinguish 'currently on the stack' from 'fully processed', causing false negatives.
- Building the adjacency list in the wrong direction — if [a,b] means 'b is prerequisite of a', then the edge goes from b to a.
- In Kahn's algorithm, not checking processedCount === numCourses — the algorithm must process all nodes to confirm no cycle.
- Forgetting to initialise the queue with all zero-in-degree nodes (not just node 0).
Follow-up questions
An interviewer at Gusto may pivot to one of these next:
- Course Schedule II (LC 210) — return the actual course ordering or an empty array if impossible.
- How would you find and report all courses in a cycle?
- Extend to weighted prerequisites: what if some prerequisites have a required completion order within a time window?
Solve it now
Free. No sign-up. Python and JavaScript run instantly in your browser.
FAQ
Why do we need 3 states instead of 2?
2 states (visited/unvisited) can't distinguish a cycle from a DAG. A node 'in-progress' (on the current DFS stack) vs 'done' (fully explored) is the key distinction — only a back edge to an in-progress node means a cycle.
What does processedCount < numCourses mean in Kahn's algorithm?
Some nodes had non-zero in-degree throughout — their prerequisites formed a cycle, so they were never enqueued. A cycle prevents complete processing.
Can this problem have multiple valid answers?
The question only asks true/false. For Course Schedule II, multiple topological orderings may exist — the BFS or DFS approach can return any valid one.
Practice these live with InterviewChamp.AI
Drill Course Schedule and other Gusto interview questions under real-loop conditions with instant feedback on your reasoning, complexity claims, and code.
Practice these live with InterviewChamp.AI →