59. Course Schedule
There are numCourses courses. [a, b] in prerequisites means you must take b before a. Can you finish all courses? (Is the dependency graph free of cycles?)
Example 1
numCourses = 2, prerequisites = [[1,0]]trueExample 2
numCourses = 2, prerequisites = [[1,0],[0,1]]falseHint 1
This is cycle detection in a directed graph.
Hint 2
Kahn’s algorithm: repeatedly take courses with no remaining prerequisites.
Approach 1: DFS with colours
A back edge to a “visiting” node means a cycle.
Approach 2: Kahn’s topological sort ✅
If every course gets processed, there is no cycle.
Reference solution for Kahn’s topological sort (time O(V + E), space O(V + E)). Each solution passes every test in all four languages.
class Solution:
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
adj = [[] for _ in range(numCourses)]
indeg = [0] * numCourses
for a, b in prerequisites: # edge b -> a
adj[b].append(a)
indeg[a] += 1
q = deque(i for i in range(numCourses) if indeg[i] == 0)
taken = 0
while q: # Kahn's algorithm
u = q.popleft()
taken += 1
for v in adj[u]:
indeg[v] -= 1
if indeg[v] == 0:
q.append(v)
return taken == numCourses # a cycle leaves some courses never readyclass Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
List<List<Integer>> adj = new ArrayList<>();
for (int i = 0; i < numCourses; i++) adj.add(new ArrayList<>());
int[] indeg = new int[numCourses];
for (int[] p : prerequisites) { adj.get(p[1]).add(p[0]); indeg[p[0]]++; }
ArrayDeque<Integer> q = new ArrayDeque<>();
for (int i = 0; i < numCourses; i++) if (indeg[i] == 0) q.add(i);
int taken = 0;
while (!q.isEmpty()) { // Kahn's algorithm
int u = q.poll(); taken++;
for (int v : adj.get(u)) if (--indeg[v] == 0) q.add(v);
}
return taken == numCourses;
}
}class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
vector<vector<int>> adj(numCourses);
vector<int> indeg(numCourses, 0);
for (auto& p : prerequisites) { adj[p[1]].push_back(p[0]); indeg[p[0]]++; }
queue<int> q;
for (int i = 0; i < numCourses; i++) if (indeg[i] == 0) q.push(i);
int taken = 0;
while (!q.empty()) { // Kahn's algorithm
int u = q.front(); q.pop(); taken++;
for (int v : adj[u]) if (--indeg[v] == 0) q.push(v);
}
return taken == numCourses;
}
};function canFinish(numCourses, prerequisites) {
const adj = Array.from({ length: numCourses }, () => []), indeg = new Array(numCourses).fill(0);
for (const [a, b] of prerequisites) { adj[b].push(a); indeg[a]++; }
const q = [];
for (let i = 0; i < numCourses; i++) if (indeg[i] === 0) q.push(i);
let taken = 0;
for (let h = 0; h < q.length; h++) { // Kahn's algorithm
taken++;
for (const v of adj[q[h]]) if (--indeg[v] === 0) q.push(v);
}
return taken === numCourses;
}Try solving it yourself first. Interviewers care more about how you explain the approach than about the finished code.
Watch the core idea of this problem step by step. Change the input and press Visualize.
Explained in the lesson Topological Sort, Dijkstra & Union-Find.
Run your code to see results here.