192. Course Schedule Feasibility
Problem
Given n courses labeled 0 through n-1 and pairs (course,prerequisite), return whether all courses can be completed. Each pair means prerequisite must precede course. Duplicate pairs may appear. Zero courses is feasible; a self-prerequisite is not.
Examples
Example 1
can_finish(3,[(1,0),(2,1)])
=> TrueThe dependency chain can be completed in order.
Example 2
can_finish(2,[(1,0),(0,1)])
=> FalseEach course waits for the other.
Example 3
can_finish(0,[])
=> TrueNo requirement obstructs the empty schedule.
Reveal the worked solution and reasoning
Why this mechanism works
Turn each requirement into prerequisite->course. A course becomes available when all its incoming requirements have been removed. Repeatedly process zero-indegree courses and release their dependents. If a cycle remains, its members retain at least one unmet requirement and cannot enter the queue.
State and boundaries
indegree[v] counts prerequisite-edge occurrences from unprocessed courses. The queue contains currently available courses.
Protocol
- Build outgoing lists and indegrees.
- Enqueue every zero-indegree course.
- Process one course and decrement each dependent's indegree.
- Enqueue a dependent exactly when its count reaches zero.
- Compare processed count with n.
Visualization
requirements (1,0),(2,1) -> 0 -> 1 -> 2
indegrees [0,1,1]
release 0, then 1, then 2 -> all completePython implementation
from collections import deque
def can_finish(n, prerequisites):
adj = [[] for _ in range(n)]
indegree = [0] * n
for course, prerequisite in prerequisites:
adj[prerequisite].append(course)
indegree[course] += 1
queue = deque(u for u in range(n) if indegree[u] == 0)
processed = 0
while queue:
u = queue.popleft()
processed += 1
for v in adj[u]:
indegree[v] -= 1
if indegree[v] == 0:
queue.append(v)
return processed == n
Worked execution
can_finish(3,[(1,0),(2,1)])
L4 can_finish: adj = [[] for _ in range(n)] n=3; prerequisites=[(1, 0), (2, 1)]; _=0 L4 can_finish: adj = [[] for _ in range(n)] n=3; prerequisites=[(1, 0), (2, 1)]; _=1 L4 can_finish: adj = [[] for _ in range(n)] n=3; prerequisites=[(1, 0), (2, 1)]; _=2 L4 can_finish: adj = [[] for _ in range(n)] n=3; prerequisites=[(1, 0), (2, 1)]; adj=[[], [], []] L5 can_finish: indegree = [0] * n n=3; prerequisites=[(1, 0), (2, 1)]; adj=[[], [], []]; indegree=[0, 0, 0] L6 can_finish: for course, prerequisite in prerequisites: n=3; prerequisites=[(1, 0), (2, 1)]; adj=[[], [], []]; indegree=[0, 0, 0]; course=1; prerequisite=0 L7 can_finish: adj[prerequisite].append(course) n=3; prerequisites=[(1, 0), (2, 1)]; adj=[[1], [], []]; indegree=[0, 0, 0]; course=1; prerequisite=0 L8 can_finish: indegree[course] += 1 n=3; prerequisites=[(1, 0), (2, 1)]; adj=[[1], [], []]; indegree=[0, 1, 0]; course=1; prerequisite=0 ... 16 intermediate state changes omitted ... L15 can_finish: indegree[v] -= 1 n=3; prerequisites=[(1, 0), (2, 1)]; adj=[[1], [2], []]; indegree=[0, 0, 0]; course=2; prerequisite=1; queue=deque([]); processed=2; u=1; v=2 L17 can_finish: queue.append(v) n=3; prerequisites=[(1, 0), (2, 1)]; adj=[[1], [2], []]; indegree=[0, 0, 0]; course=2; prerequisite=1; queue=deque([2]); processed=2; u=1; v=2 L12 can_finish: u = queue.popleft() n=3; prerequisites=[(1, 0), (2, 1)]; adj=[[1], [2], []]; indegree=[0, 0, 0]; course=2; prerequisite=1; queue=deque([]); processed=2; u=2; v=2 L13 can_finish: processed += 1 n=3; prerequisites=[(1, 0), (2, 1)]; adj=[[1], [2], []]; indegree=[0, 0, 0]; course=2; prerequisite=1; queue=deque([]); processed=3; u=2; v=2
Result: True. The dependency chain can be completed in order.
can_finish(2,[(1,0),(0,1)])
L4 can_finish: adj = [[] for _ in range(n)] n=2; prerequisites=[(1, 0), (0, 1)]; _=0 L4 can_finish: adj = [[] for _ in range(n)] n=2; prerequisites=[(1, 0), (0, 1)]; _=1 L4 can_finish: adj = [[] for _ in range(n)] n=2; prerequisites=[(1, 0), (0, 1)]; adj=[[], []] L5 can_finish: indegree = [0] * n n=2; prerequisites=[(1, 0), (0, 1)]; adj=[[], []]; indegree=[0, 0] L6 can_finish: for course, prerequisite in prerequisites: n=2; prerequisites=[(1, 0), (0, 1)]; adj=[[], []]; indegree=[0, 0]; course=1; prerequisite=0 L7 can_finish: adj[prerequisite].append(course) n=2; prerequisites=[(1, 0), (0, 1)]; adj=[[1], []]; indegree=[0, 0]; course=1; prerequisite=0 L8 can_finish: indegree[course] += 1 n=2; prerequisites=[(1, 0), (0, 1)]; adj=[[1], []]; indegree=[0, 1]; course=1; prerequisite=0 L6 can_finish: for course, prerequisite in prerequisites: n=2; prerequisites=[(1, 0), (0, 1)]; adj=[[1], []]; indegree=[0, 1]; course=0; prerequisite=1 ... 2 intermediate state changes omitted ... L9 <genexpr>: queue = deque(u for u in range(n) if indegree[u] == 0) .0=range_iterator; u=0; indegree=[1, 1] L9 <genexpr>: queue = deque(u for u in range(n) if indegree[u] == 0) .0=range_iterator; u=1; indegree=[1, 1] L9 can_finish: queue = deque(u for u in range(n) if indegree[u] == 0) n=2; prerequisites=[(1, 0), (0, 1)]; adj=[[1], [0]]; indegree=[1, 1]; course=0; prerequisite=1; queue=deque([]) L10 can_finish: processed = 0 n=2; prerequisites=[(1, 0), (0, 1)]; adj=[[1], [0]]; indegree=[1, 1]; course=0; prerequisite=1; queue=deque([]); processed=0
Result: False. Each course waits for the other.
Why the solution is correct
Each processed course has no remaining prerequisite, so the produced prefix is feasible. If processing stops early, every remaining vertex has an incoming edge from the remaining graph; following predecessors in a finite set yields a cycle. Conversely, a cycle can never release its own first member.
Time and space
O(n+m) time and space for m prerequisite pairs.
Common mistakes
Reversing pair meaning changes the intended order even though cycle existence may remain the same. Deduplicating adjacency without also deduplicating indegrees can leave counts permanently positive.
Reasoning card
State: indegree[v] counts prerequisite-edge occurrences from unprocessed courses. The queue contains currently available courses.
Invariant: A cycle is the only obstruction to a complete prerequisite order.
Decision to rehearse: Detect a directed cycle or process all zero-indegree nodes.
Finish: Compare processed count with n
Transfer exercise
Return the unprocessed courses after failure. Explain why they can include descendants of cycles as well as cycle members.