207. Course Schedule
mediumAsked at AndurilDetermine whether all courses can be completed given their prerequisites — in other words, detect whether a directed graph contains a cycle. Anduril tests this because dependency cycle detection is fundamental to build system safety, firmware task scheduling, and verifying that mission plans are acyclic before execution.
By Alex Chen, Founder, InterviewChamp.AI · Last verified
Source citations
Public interview reports confirming this problem appears in Anduril loops.
- Glassdoor (2025-12)— Anduril SWE onsite reports cite topological sort and cycle detection as recurring themes.
- Blind (2025-10)— Anduril candidates describe graph cycle-detection problems as common in systems and embedded roles.
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 course 0 then course 1. No cycle.
Example 2
numCourses = 2, prerequisites = [[1,0],[0,1]]falseExplanation: Courses 0 and 1 depend on each other — a cycle exists.
Approaches
1. DFS cycle detection (3-color marking)
Assign each node a state: unvisited (0), in current path (1), done (2). A cycle exists if DFS reaches a node in state 1 (currently on the path).
- 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 = cycle
if (state[node] === 2) return false; // already verified safe
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: O(V+E). The 3-color pattern (white/gray/black in classic graph theory) is the canonical cycle-detection algorithm. Anduril engineers appreciate when you name it.
2. Kahn's algorithm (BFS topological sort)
Compute in-degrees. Iteratively remove zero-in-degree nodes and their outgoing edges. If all nodes are removed, no cycle. If nodes remain, a 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) {
const node = queue.shift();
processed++;
for (const neighbor of adj[node]) {
inDegree[neighbor]--;
if (inDegree[neighbor] === 0) queue.push(neighbor);
}
}
return processed === numCourses;
}Tradeoff: O(V+E). Iterative and avoids call-stack depth. Better for embedded contexts where recursion depth is bounded. Kahn's also naturally produces a topological order if needed.
Anduril-specific tips
Name both approaches and state the tradeoff: 'DFS with 3-color marking is intuitive for cycle detection; Kahn's BFS is iterative and avoids stack-overflow risk on deep dependency graphs.' Anduril will often ask for the actual topological ordering as a follow-up (LC 210) — Kahn's gives it naturally. Connect to real systems: dependency resolution in firmware build systems and mission plan validation are direct applications.
Common mistakes
- Using a simple boolean visited array instead of 3-state marking — a two-state approach can't distinguish an already-completed node from a node currently on the DFS path.
- Building the adjacency list in the wrong direction — prerequisites[i] = [a,b] means b → a (b must come first).
- In Kahn's, not starting the queue with all zero-in-degree nodes — nodes with no prerequisites can be taken first.
- Confusing the check: processed === numCourses means NO cycle; processed < numCourses means cycle detected.
Follow-up questions
An interviewer at Anduril may pivot to one of these next:
- Course Schedule II (LC 210) — return the topological ordering, not just whether it exists.
- How would you detect cycles in a graph with weighted edges (for priority scheduling)?
- How would you parallelize Kahn's algorithm to process independent nodes concurrently?
Solve it now
Free. No sign-up. Python and JavaScript run instantly in your browser.
FAQ
Why 3 colors instead of 2 (visited/unvisited)?
With 2 states you can't tell whether a node was reached via the current DFS path (indicating a cycle) or via a previous completed DFS path (safe). The 'in-progress' state captures the difference.
Which approach should I present first at Anduril?
Either is fine. DFS is more naturally recursive; Kahn's is iterative and aligns with Anduril's preference for stack-safe code. Mention both.
What if prerequisites is empty?
All nodes have in-degree 0; Kahn's processes them all in the first batch; processed == numCourses; returns true. DFS visits each node cleanly and returns true.
Practice these live with InterviewChamp.AI
Drill Course Schedule and other Anduril interview questions under real-loop conditions with instant feedback on your reasoning, complexity claims, and code.
Practice these live with InterviewChamp.AI →