1136 - Parallel Courses
Difficulty: Medium | Pattern: Topological Sort (BFS / Kahn's Algorithm) | Company tags: Google, Amazon, Microsoft
Problem Statement
You are given an integer n, which indicates that there are n courses labeled from 1 to n. You are also given an array relations where relations[i] = [prevCourse_i, nextCourse_i], representing a prerequisite relationship between course prevCourse_i and course nextCourse_i: course prevCourse_i has to be studied before course nextCourse_i.
In one semester, you can study any number of courses as long as you have met all the prerequisites.
Return the minimum number of semesters needed to study all courses. If there is no way to study all courses, return -1.
Example 1:
Input: n = 3, relations = [[1,3],[2,3]]
Output: 2
Explanation:
Semester 1: Take courses 1 and 2
Semester 2: Take course 3
Example 2:
Input: n = 3, relations = [[1,2],[2,3],[3,1]]
Output: -1 (cycle detected)
Algorithm Flow
Approach: BFS Topological Sort (Kahn's) — O(V+E)
Key insight: Use BFS level-by-level processing. Each BFS level = one semester. Courses with in-degree 0 can be taken in the current semester. After taking them, decrement the in-degree of dependent courses.
from collections import deque
def minimumSemesters(n: int, relations: list[list[int]]) -> int:
graph = [[] for _ in range(n + 1)]
in_degree = [0] * (n + 1)
for prev, next_course in relations:
graph[prev].append(next_course)
in_degree[next_course] += 1
# Start with all courses that have no prerequisites
queue = deque(i for i in range(1, n + 1) if in_degree[i] == 0)
semesters = 0
courses_taken = 0
while queue:
semesters += 1
# Take all available courses this semester
for _ in range(len(queue)):
course = queue.popleft()
courses_taken += 1
for neighbor in graph[course]:
in_degree[neighbor] -= 1
if in_degree[neighbor] == 0:
queue.append(neighbor)
return semesters if courses_taken == n else -1
Dry Run
n=3, relations=[[1,3],[2,3]]
- Build graph: 1→3, 2→3; in_degree: [0, 0, 0, 2] (course 3 has 2 prereqs)
- Initial queue: [1, 2] (in_degree 0)
- Semester 1: take 1 and 2 → in_degree[3] = 2-1-1 = 0 → queue = [3]
- Semester 2: take 3 → courses_taken = 3 = n
Return 2 ✓
Cycle Detection
If courses_taken < n after BFS, there's a cycle. Courses in the cycle never reach in-degree 0, so they're never enqueued.
Edge Cases
relations = []→ all courses independent → 1 semester (all in parallel)- Linear chain
1→2→3→...→n→ n semesters - Cycle → return -1
- Disconnected graph (no relations between some components) → max depth across components
Complexity
- Time: O(V + E) where V = n (courses), E = len(relations)
- Space: O(V + E) for adjacency list and in-degree array
Related problems: LeetCode 207 (Course Schedule — just detect cycle), LeetCode 210 (Course Schedule II — return ordering), LeetCode 1136 (this — count layers).
Key Terms
| Term | Definition |
|---|---|
| Topological sort | An ordering of a directed graph's nodes such that every edge points from an earlier node to a later one; only possible for DAGs. |
| Kahn's algorithm | A BFS-based topological sort that repeatedly removes nodes with in-degree 0. |
| In-degree | The number of incoming edges (unfinished prerequisites) a node currently has. |
| BFS level / semester | One full round of processing all currently-available nodes at once, used here to model "one semester." |
| Cycle detection | Identifying that not all nodes can be processed because some form a circular dependency, indicated by courses_taken < n. |
FAQ
- Can this be solved without extra space? No — you need at least O(V+E) for the adjacency list and in-degree array; this is unavoidable for graph traversal problems.
- What if the input graph has a cycle?
Nodes in the cycle never reach in-degree 0, so they're never enqueued; the algorithm detects this by checking
courses_taken == nat the end and returns -1 otherwise. - What if we only needed to know whether all courses can be finished (not minimum semesters)? That's LeetCode 207 (Course Schedule) — same in-degree BFS, but you just check reachability of all nodes, no need to count levels.
- How would this change if we needed the actual course ordering rather than semester count? That's LeetCode 210 (Course Schedule II) — same Kahn's algorithm, but append each dequeued node to a result list instead of counting levels.
- Could DFS be used instead of BFS here? DFS-based topological sort (with a visited/visiting/done coloring) can also detect cycles and produce an ordering, but computing "minimum semesters" is more natural with BFS since each level maps directly to a semester.
Quick Revision
- Problem: minimum semesters to finish all courses given prerequisite pairs, or -1 if impossible.
- Build a directed graph and an in-degree array from
relations. - Initialize the queue with all nodes that have in-degree 0 (no prerequisites).
- Process the queue level by level — each level = one semester.
- For each node processed, decrement in-degree of its neighbors; enqueue any that reach 0.
- Count total courses taken; if it's less than
n, a cycle exists — return -1. - Time: O(V + E); Space: O(V + E).
- This is Kahn's algorithm with level-batching instead of single-node dequeuing.
Related Problems
- Pattern: Topological Sort / Kahn's Algorithm, also seen in "Course Schedule" (LeetCode 207) and "Course Schedule II" (LeetCode 210).
- Both LeetCode 207 and 210 share the same in-degree/BFS graph traversal core as this problem.