207. Course Schedule
mediumAsked at AMDDetermine if you can finish all courses given a list of prerequisites. This is cycle detection in a directed graph — AMD tests it because dependency ordering is fundamental to task graph scheduling, GPU compute graph compilation, and LLVM IR pass ordering in their compiler toolchain.
By Alex Chen, Founder, InterviewChamp.AI · Last verified
Source citations
Public interview reports confirming this problem appears in AMD loops.
- Glassdoor (2026-Q1)— AMD SWE candidates report Course Schedule appearing in medium rounds with a follow-up about GPU task graph scheduling.
- Blind (2025-10)— AMD interview threads highlight topological sort and cycle detection as high-frequency graph topics reflecting compiler work.
Problem
There are a total of numCourses courses you have to take, labeled from 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, otherwise return false.
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 0 first, then 1.
Example 2
numCourses = 2, prerequisites = [[1,0],[0,1]]falseExplanation: Circular dependency — each requires the other.
Approaches
1. DFS cycle detection (coloring)
Build an adjacency list. DFS with three states: 0=unvisited, 1=in-progress, 2=done. If you visit an in-progress node, there's a cycle.
- Time
- O(V + E)
- Space
- O(V + E)
function canFinish(numCourses, prerequisites) {
const graph = Array.from({ length: numCourses }, () => []);
for (const [a, b] of prerequisites) graph[b].push(a);
const state = new Array(numCourses).fill(0); // 0=new, 1=active, 2=done
function dfs(node) {
if (state[node] === 1) return false; // cycle
if (state[node] === 2) return true; // already validated
state[node] = 1;
for (const neighbor of graph[node]) {
if (!dfs(neighbor)) return false;
}
state[node] = 2;
return true;
}
for (let i = 0; i < numCourses; i++) {
if (!dfs(i)) return false;
}
return true;
}Tradeoff: O(V+E). The three-color state avoids re-visiting already-validated subtrees. Clearly maps to the 'gray node = back edge = cycle' convention from textbook graph theory.
2. BFS Topological Sort (Kahn's algorithm)
Compute in-degrees. Start BFS from nodes with in-degree 0. Decrement neighbors' in-degrees; when a neighbor reaches 0, enqueue it. 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 graph = Array.from({ length: numCourses }, () => []);
for (const [a, b] of prerequisites) { graph[b].push(a); inDegree[a]++; }
const queue = [];
for (let i = 0; i < numCourses; i++) if (inDegree[i] === 0) queue.push(i);
let processed = 0;
while (queue.length) {
const node = queue.shift();
processed++;
for (const neighbor of graph[node]) {
if (--inDegree[neighbor] === 0) queue.push(neighbor);
}
}
return processed === numCourses;
}Tradeoff: O(V+E). Iterative — no call-stack risk for large graphs. The processed count check elegantly detects cycles without explicit cycle tracking.
AMD-specific tips
AMD's ROCm compute graph API and LLVM pass manager both schedule tasks using topological ordering. Mention the real-world connection: 'a GPU compute graph is a DAG of kernels; the scheduler needs a topological order to dispatch them. A cycle means deadlock.' Kahn's algorithm is particularly natural here because it mirrors how a real scheduler processes nodes with no pending dependencies (in-degree 0). AMD interviewers appreciate when candidates name the algorithm (Kahn's) rather than just describing it.
Common mistakes
- Building the graph in the wrong direction — [a,b] means b is a prerequisite of a, so the edge goes from b to a.
- Not handling disconnected components — the outer loop must visit every node.
- DFS: returning false when state[node] === 2 instead of true — a fully processed node is safe.
- Kahn's: comparing processed to prerequisites.length instead of numCourses — you need all nodes processed.
Follow-up questions
An interviewer at AMD may pivot to one of these next:
- Course Schedule II (LC 210) — return the actual topological order.
- Alien Dictionary (LC 269) — derive a topological order from lexicographic constraints.
- How does AMD's ROCm HIP graph API use dependency graphs for kernel scheduling?
Solve it now
Free. No sign-up. Python and JavaScript run instantly in your browser.
FAQ
Why does Kahn's algorithm detect cycles?
Nodes in a cycle always have in-degree > 0 among themselves (they depend on each other), so they're never enqueued. If processed < numCourses, unprocessed nodes form at least one cycle.
Which approach is better for GPU task scheduling?
Kahn's BFS is more natural for scheduling: nodes with in-degree 0 are ready to run immediately. In a parallel setting, all zero-in-degree nodes can be dispatched simultaneously.
Practice these live with InterviewChamp.AI
Drill Course Schedule and other AMD interview questions under real-loop conditions with instant feedback on your reasoning, complexity claims, and code.
Practice these live with InterviewChamp.AI →