Course Schedule
Medium · Graphs · Explanation and code example
Topological Sort (Kahn's Algorithm)
How it works
Treat each course as a node and each prerequisite as a directed edge. If a course has no prerequisites, it can be taken immediately.
Kahn's Algorithm repeatedly takes courses that have zero prerequisites. When we finish a course, we remove its dependency effect from other courses.
- If all courses can be taken this way - no cycle, return
true - If some courses are never taken - cycle exists, return
false
Steps
- Build a graph and compute
indegree(number of prerequisites) for each course. - Add all courses with
indegree = 0into a queue. - While the queue is not empty:
- Remove a course from the queue.
- Mark it as finished.
- Reduce the
indegreeof its dependent courses. - If any dependent course reaches
indegree = 0, add it to the queue.
- After processing:
- If finished courses == total courses - return
true - Else - return
false
- If finished courses == total courses - return
Complexity
- Time complexity:
O(V + E) - Space complexity:
O(V + E)
Where
Vis the number of courses andEis the number of prerequisites.
Things to watch for
Using a Single Visited Set Instead of Tracking the Current Path
A single visited set marks nodes as seen globally, but cycle detection requires knowing if a node is in the current DFS path. Without tracking the recursion path separately, you may miss cycles or falsely detect them.
Forgetting to Handle Disconnected Components
If you only start DFS from one node, you may miss cycles in disconnected parts of the graph. Always iterate through all courses and run DFS from each unvisited node.
Incorrectly Building the Adjacency List
The prerequisite [a, b] means "to take course a, you must first take course b". Mixing up which course maps to which leads to checking the wrong dependencies and missing or falsely reporting cycles.
Python code example
from typing import List
from collections import deque
class Solution:
def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:
indegree = [0] * numCourses
adj = [[] for i in range(numCourses)]
for src, dst in prerequisites:
indegree[dst] += 1
adj[src].append(dst)
q = deque()
for n in range(numCourses):
if indegree[n] == 0:
q.append(n)
finish = 0
while q:
node = q.popleft()
finish += 1
for nei in adj[node]:
indegree[nei] -= 1
if indegree[nei] == 0:
q.append(nei)
return finish == numCourses
C++ code example
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
vector<int> indegree(numCourses, 0);
vector<vector<int>> adj(numCourses);
for (auto& pre : prerequisites) {
indegree[pre[1]]++;
adj[pre[0]].push_back(pre[1]);
}
queue<int> q;
for (int i = 0; i < numCourses; ++i) {
if (indegree[i] == 0) {
q.push(i);
}
}
int finish = 0;
while (!q.empty()) {
int node = q.front();
q.pop();
finish++;
for (int nei : adj[node]) {
indegree[nei]--;
if (indegree[nei] == 0) {
q.push(nei);
}
}
}
return finish == numCourses;
}
};
Source and license
Adapted from NeetCode’s source · MIT license. Node types are supplied by LeetCode where needed.