Topological BFS (Kahn's Algorithm) Pattern · Interactive Visualizer & Interview Guide
[a, b] means take b before a.
So draw the arrow b -> a. Then count how many arrows point into each course.
Courses with indegree = 0 are immediately available.
[course, prereq], I add an edge prereq -> course.
I also keep an indegree[] array where indegree[x] tells me how many prerequisites course x still needs."
indegree == 0 because those are the only courses I can take immediately.
Then I repeatedly pop one, mark it as processed, and reduce the indegree of every dependent course.
If a dependent course drops to 0, it just became ready, so I enqueue it."
numCourses nodes,
there was no cycle and the answer is true. If the queue becomes empty early, some courses are stuck in a cycle, so the answer is false."
count == numCoursesorder.size() == numCoursesclass Solution {
public boolean canFinish(int numCourses, int[][] prerequisites) {
List<List<Integer>> graph = new ArrayList<>();
for (int i = 0; i < numCourses; i++) {
graph.add(new ArrayList<>());
}
int[] indegree = new int[numCourses];
for (int[] pair : prerequisites) {
int course = pair[0];
int prereq = pair[1];
graph.get(prereq).add(course);
indegree[course]++;
}
Queue<Integer> queue = new ArrayDeque<>();
for (int i = 0; i < numCourses; i++) {
if (indegree[i] == 0) {
queue.offer(i);
}
}
int processed = 0;
while (!queue.isEmpty()) {
int course = queue.poll();
processed++;
for (int next : graph.get(course)) {
indegree[next]--;
if (indegree[next] == 0) {
queue.offer(next);
}
}
}
return processed == numCourses;
}
}
class Solution {
public int[] findOrder(int numCourses, int[][] prerequisites) {
List<List<Integer>> graph = new ArrayList<>();
for (int i = 0; i < numCourses; i++) {
graph.add(new ArrayList<>());
}
int[] indegree = new int[numCourses];
for (int[] pair : prerequisites) {
int course = pair[0];
int prereq = pair[1];
graph.get(prereq).add(course);
indegree[course]++;
}
Queue<Integer> queue = new ArrayDeque<>();
for (int i = 0; i < numCourses; i++) {
if (indegree[i] == 0) {
queue.offer(i);
}
}
int[] order = new int[numCourses];
int idx = 0;
while (!queue.isEmpty()) {
int course = queue.poll();
order[idx++] = course;
for (int next : graph.get(course)) {
indegree[next]--;
if (indegree[next] == 0) {
queue.offer(next);
}
}
}
return idx == numCourses ? order : new int[0];
}
}
"prerequisites", "depends on", "must come before", "valid order", "finish all tasks", and "cycle" are strong topological-sort signals.
In ordinary BFS the queue holds locations to visit. Here the queue holds courses whose prerequisites are fully satisfied. That makes the queue semantics very easy to explain.
If a cycle exists, every node in that cycle will keep indegree > 0 forever. The queue dries up before all nodes are processed, so you do not need separate cycle-path bookkeeping.
The BFS pop order is already a valid topological order. So once you understand LC 207, LC 210 is basically the same code plus one append.
unvisited / visiting / visited.
Kahn's BFS is often simpler to reason about aloud.
O(n^2) just because prerequisites is a 2D array. That array is really an edge list, so the correct term is O(V + E).