Problem Statement
Course Schedule II
You have numCourses courses to take, numbered from 0. Some courses can only be taken after you finish other courses first. Those rules come in the array prerequisites. Each pair [a, b] means "to take course a, you must finish course b first." Your job is to return one valid order in which to take all the courses. If more than one order works, any of them is fine. If it is impossible to finish them all, return an empty array.
Signals to notice
Brute force first
Try all permutations and check validity. Brute-force permutation with constraint checking. That instinct is useful because it follows the prompt literally, but it usually keeps revisiting work the problem is begging you to organize.
The key insight
Topological sort with BFS (Kahn's): start with courses having no prerequisites (in-degree 0), process them, reduce neighbors' in-degrees. If all courses are processed, the order is valid. Instead of recomputing the world every time, you preserve just enough context to let the next decision become obvious.
Trace it on numCourses=4, prerequisites=[[1,0],[2,0],[3,1],[3,2]]
Build adj (edge a->prereq b): adj[0]=[], adj[1]=[0], adj[2]=[0], adj[3]=[1,2]; state=[0,0,0,0]; result=[] i=0 dfs(0): no neighbors -> state[0]=2 (done); result=[0] i=1 dfs(1): nei 0 done -> state[1]=2; result=[0,1] i=2 dfs(2): nei 0 done -> state[2]=2; result=[0,2... wait, appended after both done] result=[0,1,2] i=3 dfs(3): nei 1 done, nei 2 done -> state[3]=2; result=[0,1,2,3] All dfs returned true (no cycle). Because adj edges point course->prerequisite, post-order already lists prerequisites before dependents, so DO NOT reverse. Return [0,1,2,3] (valid: every prerequisite precedes its course; matches a valid topological order like the example's [0,2,1,3])
What must stay true
A course can be taken only when all its prerequisites are done. In-degree 0 means all prerequisites are satisfied. Processing these first and updating neighbors' in-degrees simulates the natural ordering. As long as that statement keeps holding, you can trust the steps built on top of it.
Shape of the loop
build adj where adj[a] holds prerequisites b of course a (edge a->b) state[*] = UNVISITED; result = [] dfs(node): if VISITING return false (cycle); if DONE return true mark VISITING; for nei in adj[node]: if !dfs(nei) return false mark DONE; result.append(node); return true // appends prereqs before dependents for each course i: if !dfs(i) return [] return result // already prereq-first; do NOT reverse
Pseudocode only — the full worked solution lives in the Solution tab.
Easy way to go wrong
Not detecting cycles — if the result has fewer courses than total, there's a circular dependency. Return an empty array, not a partial result. The fix is usually to return to the meaning of each move, not just the steps themselves.