207. Course Schedule
mediumAsked at CanvaDetect whether a set of course prerequisites forms a cycle — Canva applies topological-sort cycle detection to their layer-dependency graph, where circular element references would cause infinite render loops in their design engine.
By Alex Chen, Founder, InterviewChamp.AI · Last verified
Problem
There are numCourses courses labeled 0 through numCourses-1. You are given an array prerequisites where prerequisites[i] = [a, b] means you must take course b before course a. Return true if you can finish all courses, or false if a cycle exists.
Constraints
1 <= numCourses <= 20000 <= prerequisites.length <= 5000prerequisites[i].length == 20 <= a_i, b_i < numCoursesAll prerequisite pairs are unique
Examples
Example 1
numCourses = 2, prerequisites = [[1,0]]trueExplanation: Take course 0 first, 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.
Approaches
1. DFS cycle detection (3-color marking)
Build an adjacency list; DFS from each unvisited node using three states (unvisited, in-progress, done) — a back-edge to an in-progress node signals a cycle.
- Time
- O(V+E)
- Space
- O(V+E)
function canFinish(numCourses, prerequisites) {
const adj = Array.from({ length: numCourses }, () => []);
for (const [a, b] of prerequisites) {
adj[b].push(a);
}
// 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
if (state[node] === 2) return false; // already processed
state[node] = 1;
for (const neighbor of adj[node]) {
if (hasCycle(neighbor)) return true;
}
state[node] = 2;
return false;
}
for (let i = 0; i < numCourses; i++) {
if (hasCycle(i)) return false;
}
return true;
}Tradeoff:
2. Optimal (BFS topological sort — Kahn's algorithm)
Compute in-degrees; enqueue all zero-in-degree nodes; repeatedly dequeue a node and decrement neighbors' in-degrees, enqueuing any that reach zero — if all nodes are processed, no cycle exists.
- Time
- O(V+E)
- Space
- O(V+E)
function canFinish(numCourses, prerequisites) {
const adj = Array.from({ length: numCourses }, () => []);
const inDegree = new Array(numCourses).fill(0);
for (const [a, b] of prerequisites) {
adj[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 > 0) {
const node = queue.shift();
processed++;
for (const neighbor of adj[node]) {
inDegree[neighbor]--;
if (inDegree[neighbor] === 0) queue.push(neighbor);
}
}
return processed === numCourses;
}Tradeoff:
Canva-specific tips
Canva's rendering pipeline evaluates element dependencies in topological order — a frame element's content must render before the slide that references it. Interviewers will extend this to 'what if we need to return the actual order?' (Course Schedule II), so design your solution so the topological order falls out naturally. Kahn's BFS approach is generally preferred in production over DFS because it naturally surfaces the ordered sequence and avoids call-stack concerns on large graphs. Name Kahn's algorithm explicitly — it signals you know the canonical vocabulary.
Solve it now
Free. No sign-up. Python and JavaScript run instantly in your browser.
Practice these live with InterviewChamp.AI
Drill Course Schedule and other Canva interview questions under real-loop conditions with instant feedback on your reasoning, complexity claims, and code.
Practice these live with InterviewChamp.AI →