Parallel Courses is a topological-sort problem in disguise. The twist over a plain topo sort: you may take many courses at once, so the real question is not the order — it is how few rounds you need.
Problem. There are n courses labeled 1..n. prerequisites[i] = [u, v] means course u must be
taken before course v. In one semester you may take any number of courses whose prerequisites are
all already done. Return the minimum number of semesters to take every course, or -1 if it is impossible.
Example: n = 5, prerequisites [[1,3],[2,4],[3,5],[4,5]] → answer 3 (take {1,2}, then {3,4}, then {5}).
The slow way first
You could try taking courses one at a time in a valid order — a normal topological sort — and count steps. But that ignores the gift the problem hands you: courses with no remaining prerequisites can be taken together. Doing them one per semester would massively over-count. We need to take a whole layer of unblocked courses each round.
The question to ask: which courses can I take right now? Exactly the ones with no unmet prerequisite. Take all of them, see what that unlocks, repeat.
The idea: peel the DAG one level at a time
This is Kahn's algorithm run in levels (a layered BFS). For each course track its in-degree — the number of prerequisites still pointing at it. Every course with in-degree 0 can be taken now; that batch is one semester. Taking it relaxes (decrements) the in-degree of its dependents, which unlocks the next batch.
The key insight: the number of BFS rounds is the minimum number of semesters. And if some course never reaches in-degree 0, it sits in a prerequisite cycle — impossible, so we return -1.
Walk through it
Step through the animation. Courses 1 and 2 start with in-degree 0, so they are semester 1. Finishing them unlocks 3 and 4 (semester 2), which unlocks 5 (semester 3). Three rounds, three semesters. The semester counter on the right ticks once per BFS layer.
Pseudocode
build graph and in-degree from prerequisites
queue = every course with in-degree 0
taken = 0, semesters = 0
while queue is not empty:
semesters += 1 # one whole layer = one semester
next_queue = empty
for each course u in queue:
taken += 1
for each dependent v of u:
in-degree[v] -= 1
if in-degree[v] == 0:
add v to next_queue
queue = next_queue
return semesters if taken == n else -1 # -1 means a cycleThe Python solution
def min_semesters(n, prerequisites):
graph = {c: [] for c in range(1, n + 1)}
indeg = {c: 0 for c in range(1, n + 1)}
for u, v in prerequisites:
graph[u].append(v)
indeg[v] += 1
queue = [c for c in graph if indeg[c] == 0]
taken = semesters = 0
while queue:
semesters += 1
nxt = []
for u in queue:
taken += 1
for v in graph[u]:
indeg[v] -= 1
if indeg[v] == 0:
nxt.append(v)
queue = nxt
return semesters if taken == n else -1graphis an adjacency list;indeg[v]counts the prerequisites still pointing atv.queuestarts with every course that has no prerequisite (in-degree 0).- Each iteration of the
whileloop is one semester — that is whysemesters += 1happens once per round, not once per course. - The inner loop takes the entire current batch at once and relaxes every dependent.
- A dependent that hits in-degree
0joinsnxt, the next semester batch. taken == nconfirms we took every course. If not, some course was stuck in a cycle, so we return-1.
Complexity
| Case | Time | Notes |
|---|---|---|
| Build graph + in-degrees | O(V + E) (moderate) | scan courses and prereqs once |
| Layered BFS (Kahn) | O(V + E) (moderate) | each node and edge processed once |
O(V + E) (moderate)Every course is queued exactly once and every prerequisite edge is relaxed exactly once, so the whole algorithm is linear in the size of the graph.
When this pattern shows up
Whenever a problem is about ordering under dependencies — course schedules, build steps, task pipelines — reach for topological sort. If it also asks for the fewest rounds / minimum time / longest chain, run Kahn level by level: the number of BFS layers is the answer.
Do not forget the cycle check. If you only count rounds, a graph with a prerequisite cycle quietly returns a
wrong number. Track how many courses you actually took and compare against n — if fewer, return -1.
Practice
After taking semester 1 = {1, 2}, what are the in-degrees of courses 3, 4, and 5, and which ones are ready next?
1. What does the number of BFS rounds represent?
2. Which courses can be taken in the first semester?
3. How do we detect that the schedule is impossible?
4. Why increment semesters once per while-iteration rather than once per course?