Skip to content

190. Course Schedule II

๐Ÿ”— LeetCode Problem: 210. Course Schedule II
๐Ÿ“Š Difficulty: Medium
๐Ÿท๏ธ Topics: Graph, DFS, BFS, Topological Sort

Problem Statement

There are a total of numCourses courses you have to take, labeled from 0 to numCourses - 1. You are given an array prerequisites where prerequisites[i] = [aแตข, bแตข] indicates that you must take course bแตข first if you want to take course aแตข.

  • For example, the pair [0, 1] indicates that to take course 0 you have to first take course 1.

Return the ordering of courses you should take to finish all courses. If there are many valid answers, return any of them. If it is impossible to finish all courses, return an empty array.

Example 1:

Input: numCourses = 2, prerequisites = [[1,0]]
Output: [0,1]

Explanation: 
  Total 2 courses. To take course 1, need course 0 first.
  Valid order: [0, 1] โœ…

Example 2:

Input: numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]
Output: [0,2,1,3]

Explanation:
  Multiple valid orders exist:
  - [0,1,2,3]
  - [0,2,1,3] โœ… (one valid answer)

  Both are correct!

Example 3:

Input: numCourses = 1, prerequisites = []
Output: [0]

Explanation:
  Only one course, no prerequisites.
  Just take it! โœ…

Constraints: - 1 <= numCourses <= 2000 - 0 <= prerequisites.length <= numCourses * (numCourses - 1) - prerequisites[i].length == 2 - 0 <= aแตข, bแตข < numCourses - aแตข != bแตข - All the pairs [aแตข, bแตข] are distinct.


๐ŸŒณ Visual Understanding - The Complete Picture

The Problem We're Solving ๐Ÿค”

Same as Course Schedule, but now RETURN THE ORDER! ๐Ÿ“‹

Example:
  numCourses = 4
  prerequisites = [[1,0], [2,0], [3,1], [3,2]]

Graph:
       0
      / \
     โ†“   โ†“
     1   2
      \ /
       โ†“
       3

Course Schedule asked: "Can we finish?" โœ…/โŒ
Course Schedule II asks: "WHAT ORDER?" ๐Ÿ“Š

Valid ordering: [0, 1, 2, 3] or [0, 2, 1, 3]
Both respect all prerequisites! โœ…

Building Understanding Through Examples ๐Ÿ“š

Example 1: Simple chain

prerequisites = [[1,0]]

Graph:
  0 โ†’ 1

Order: [0, 1] โœ…
  Step 1: Take 0 (no prerequisites)
  Step 2: Take 1 (prerequisite 0 completed)

Simple and clear! ๐ŸŽฏ
Example 2: Diamond graph

prerequisites = [[1,0], [2,0], [3,1], [3,2]]

Graph:
       0
      / \
     1   2
      \ /
       3

Multiple valid orders:
  [0, 1, 2, 3] โœ… Take 1 before 2
  [0, 2, 1, 3] โœ… Take 2 before 1

Both valid because:
  - 0 has no prerequisites (take first)
  - 1 and 2 both need 0 (can do in any order)
  - 3 needs both 1 and 2 (take last)

Return ANY valid order! ๐Ÿ“Š
Example 3: Impossible (cycle)

prerequisites = [[1,0], [0,1]]

Graph:
  0 โ‡„ 1 (cycle!)

No valid order exists! โŒ
Return: [] (empty array)

Same as Course Schedule returning false!
Visual: Topological Order

Graph:
       0
      / \
     1   2
      \ /
       3

Level-by-level view:
  Level 0: {0} - no dependencies
  Level 1: {1, 2} - depend on level 0
  Level 2: {3} - depends on level 1

One valid order: Process level by level!
  [0, 1, 2, 3] or [0, 2, 1, 3] โœ…

This is TOPOLOGICAL ORDERING! ๐ŸŽฏ

The Pattern Emerges ๐Ÿ’ก

Key observations: ๐Ÿ”

1๏ธโƒฃ Extension of Course Schedule ๐Ÿ“š
   Same cycle detection
   PLUS: collect the ordering!

2๏ธโƒฃ Topological Sort Output ๐Ÿ“Š
   Course Schedule: boolean
   Course Schedule II: actual order

3๏ธโƒฃ Two Approaches Still Work ๐Ÿ› ๏ธ
   DFS: Post-order collection
   BFS (Kahn's): Processing order IS the answer!

4๏ธโƒฃ Return Empty on Cycle ๐Ÿ”„
   If cycle detected: return []
   If DAG: return valid order

5๏ธโƒฃ Multiple Valid Answers โœจ
   Any topological order works!
   Just return ONE valid order

๐Ÿง  The Journey From Brute Force to Optimal

Part 1: The Connection - "Building on Course Schedule!" ๐Ÿค”

๐Ÿ’ญ We already solved the hard part!

Course Schedule (Problem 189):
  โœ… Build graph
  โœ… Detect cycles
  โœ… Return true/false

Course Schedule II:
  โœ… Build graph (same!)
  โœ… Detect cycles (same!)
  ๐Ÿ†• COLLECT the ordering!
  ๐Ÿ†• Return order or []

The NEW part: Collecting the order! ๐Ÿ“‹

Two approaches from before:
  1. DFS with three states
  2. BFS (Kahn's algorithm)

Both need modification to collect order! ๐Ÿ”ง

Part 2: The DFS Approach - "Post-order is topological!" ๐Ÿ’ญ

๐Ÿค” DFS insight: Post-order traversal gives topological order!

Why post-order?
  When we finish exploring a node:
    - All its dependencies explored
    - All courses it depends on done
    - Safe to add to order!

Example:
  Graph: 0 โ†’ 1 โ†’ 2

  DFS from 0:
    Explore 0 โ†’ 1 โ†’ 2
    Finish 2 first (no dependencies)
    Finish 1 next (2 done)
    Finish 0 last (1 done)

  Post-order: [2, 1, 0]
  Reverse: [0, 1, 2] โœ… Valid topological order!

Key modification:
  When marking node VISITED:
    Add to result list!

  After DFS completes:
    Reverse the list!

  This gives topological order! ๐ŸŽฏ

Algorithm:
  List<Integer> order = new ArrayList<>();

  void dfs(node):
    mark VISITING
    for each neighbor:
      if VISITING: return false (cycle)
      if UNVISITED: dfs(neighbor)
    mark VISITED
    order.add(node)  // Add in post-order! ๐Ÿ”‘
    return true

  Collections.reverse(order)  // Reverse for correct order!
  return order

Simple extension of Course Schedule! โœจ

Part 3: The BFS Approach - "Processing order IS the answer!" ๐Ÿ’ก

๐Ÿค” BFS (Kahn's) insight: Even simpler!

The order we PROCESS courses = valid topological order! ๐ŸŽฏ

Why?
  Kahn's algorithm processes in topological order:
    1. Start with zero in-degree (no dependencies)
    2. Process them
    3. Remove edges, find new zero in-degree
    4. Repeat

  The processing order respects all dependencies! โœ…

Example:
  Graph: 0 โ†’ 1 โ†’ 2

  In-degrees: [0: 0, 1: 1, 2: 1]

  Step 1: Process 0 (in-degree 0)
          order = [0]
          Update in-degrees: [1: 0, 2: 1]

  Step 2: Process 1 (in-degree 0)
          order = [0, 1]
          Update in-degrees: [2: 0]

  Step 3: Process 2 (in-degree 0)
          order = [0, 1, 2] โœ…

  This IS the topological order! No reversal needed!

Key modification:
  int[] order = new int[numCourses];
  int index = 0;

  while (queue not empty):
    course = queue.poll()
    order[index++] = course  // Add to result! ๐Ÿ”‘

    // Update in-degrees as before...

  if (index == numCourses):
    return order  // All processed! โœ…
  else:
    return new int[0]  // Cycle! โŒ

Even simpler than DFS! No reversal needed! ๐ŸŒŸ

Part 4: The Comparison - "Which to use?" ๐Ÿ’ก

๐Ÿค” DFS vs BFS for Course Schedule II:

DFS Approach:
  โœ… Natural cycle detection
  โœ… Familiar recursion
  โŒ Need to reverse list
  โŒ Post-order concept might confuse

  Result collection:
    Add in post-order โ†’ reverse

BFS Approach (Kahn's):
  โœ… Processing order IS answer (no reverse!)
  โœ… Clearer logic
  โœ… Iterative (no stack overflow)
  โœ… More intuitive

  Result collection:
    Add while processing โ†’ done!

For Course Schedule II:
  BFS is CLEANER! ๐ŸŽฏ

  Processing order naturally gives topological sort!
  No reversal, no post-order confusion!

Recommendation: Use BFS (Kahn's)! โญ

Both work and both O(V + E), but BFS is cleaner
for problems that need the actual ordering! ๐Ÿ“Š

Part 5: The Pattern - "Topological sort output!" ๐ŸŽฏ

โœจ This solidifies the topological sort pattern!

Course Schedule (189):
  Input: Graph (prerequisites)
  Output: Boolean (possible?)
  Method: Cycle detection

Course Schedule II (190): โ† YOU ARE HERE
  Input: Graph (prerequisites)
  Output: Ordering (what order?)
  Method: Topological sort with output

Pattern evolution:
  Level 1: Detect if ordering exists
  Level 2: Return the actual ordering

Many problems follow this pattern:
  - Alien Dictionary: Return alphabet order
  - Sequence Reconstruction: Verify unique order
  - Parallel Courses: Track levels/time

Template for "return ordering" problems:

BFS Template:
  Calculate in-degrees
  Queue up zero in-degree nodes

  List<Integer> result = new ArrayList<>();
  while (queue not empty):
    node = queue.poll()
    result.add(node)  // Collect order! ๐Ÿ”‘

    Update neighbors' in-degrees
    Add newly-zero to queue

  return result.size() == n ? result : empty

DFS Template:
  List<Integer> result = new ArrayList<>();

  For each unvisited:
    if (hasCycle): return empty

  // In DFS:
  When finishing node:
    result.add(node)

  Collections.reverse(result)
  return result

Pattern mastered! ๐ŸŒŸ

The Key Insight ๐Ÿ’ก

Use Kahn's algorithm where processing order IS topological order! ๐ŸŒŠ Calculate in-degrees, queue zero in-degree courses ๐Ÿ“Š. Process courses: add to result, decrease neighbors' in-degrees, queue newly-zero โœ…. If processed all courses, return order ๐Ÿ“‹. Otherwise return empty array (cycle) ๐Ÿ”„. No reversal needed - processing order is answer! Time: O(V+E) โšก, Space: O(V+E) ๐Ÿ“ฆ!

Cleanest approach! โœจ

Complete Implementation

class Solution {
    public int[] findOrder(int numCourses, int[][] prerequisites) {
        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        // KAHN'S ALGORITHM - CLEANEST FOR THIS PROBLEM! โญ
        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        // Processing order IS topological order
        // No reversal needed!
        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        // BUILD ADJACENCY LIST ๐Ÿ—๏ธ
        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        List<List<Integer>> graph = new ArrayList<>();
        for (int i = 0; i < numCourses; i++) {
            graph.add(new ArrayList<>());
        }

        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        // CALCULATE IN-DEGREES ๐Ÿ“Š
        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        int[] inDegree = new int[numCourses];

        for (int[] prereq : prerequisites) {
            int course = prereq[0];
            int prerequisite = prereq[1];

            graph.get(prerequisite).add(course);  // prereq โ†’ course
            inDegree[course]++;  // One more prerequisite
        }

        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        // QUEUE COURSES WITH NO PREREQUISITES ๐Ÿ”
        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        Queue<Integer> queue = new LinkedList<>();

        for (int course = 0; course < numCourses; course++) {
            if (inDegree[course] == 0) {
                queue.offer(course);  // Can take immediately! โœ…
            }
        }

        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        // RESULT ARRAY ๐Ÿ“‹
        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        int[] order = new int[numCourses];
        int index = 0;

        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        // BFS: PROCESS COURSES IN TOPOLOGICAL ORDER ๐ŸŒŠ
        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        while (!queue.isEmpty()) {
            int course = queue.poll();

            // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
            // ADD TO RESULT ๐Ÿ“
            // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
            // This course can be taken now!
            // Add to ordering!
            // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
            order[index++] = course;

            // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
            // DECREASE IN-DEGREE OF DEPENDENT COURSES ๐Ÿ“‰
            // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
            for (int dependent : graph.get(course)) {
                inDegree[dependent]--;

                // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
                // IF NOW READY, ADD TO QUEUE ๐ŸŽฏ
                // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
                if (inDegree[dependent] == 0) {
                    queue.offer(dependent);
                }
            }
        }

        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        // CHECK IF ALL COURSES PROCESSED โœ…
        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        // If processed all: return order โœ…
        // If some remain: cycle exists, return empty โŒ
        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        if (index == numCourses) {
            return order;  // Success! ๐ŸŽ“
        } else {
            return new int[0];  // Cycle detected! ๐Ÿ”„
        }
    }
}

Why This Works ๐ŸŽฏ

Processing order = topological order: ๐ŸŒŠ - Kahn's processes in dependency order - Zero in-degree โ†’ no unmet dependencies - Safe to "take" course now! - Order respects all prerequisites โœ…

No reversal needed: โšก - Unlike DFS which gives post-order - BFS gives correct order directly! - Cleaner and more intuitive!

Cycle detection built-in: ๐Ÿ”„ - If cycle: some courses never reach zero in-degree - index < numCourses at end - Return empty array!


๐ŸŽฏ Approach 2: DFS with Post-order Collection

The Key Insight ๐Ÿ’ก

Use DFS with post-order collection ๐Ÿ”. Add course to result when marking VISITED (all dependencies done) โœ…. After all DFS, reverse the result ๐Ÿ”„. Reversed post-order = topological order! ๐Ÿ“Š If cycle detected, return empty array โŒ. Time: O(V+E) โšก, Space: O(V+E) ๐Ÿ“ฆ!

Classic approach! โœจ

Complete Implementation

class Solution {
    // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
    // THREE STATES FOR CYCLE DETECTION ๐Ÿ”„
    // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
    private static final int UNVISITED = 0;
    private static final int VISITING = 1;
    private static final int VISITED = 2;

    public int[] findOrder(int numCourses, int[][] prerequisites) {
        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        // DFS POST-ORDER COLLECTION APPROACH ๐Ÿ”
        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        // BUILD ADJACENCY LIST ๐Ÿ—๏ธ
        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        List<List<Integer>> graph = new ArrayList<>();
        for (int i = 0; i < numCourses; i++) {
            graph.add(new ArrayList<>());
        }

        for (int[] prereq : prerequisites) {
            int course = prereq[0];
            int prerequisite = prereq[1];
            graph.get(prerequisite).add(course);
        }

        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        // STATE TRACKING & RESULT LIST ๐Ÿ“Š
        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        int[] state = new int[numCourses];
        List<Integer> order = new ArrayList<>();

        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        // DFS FROM EACH UNVISITED COURSE ๐Ÿš€
        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        for (int course = 0; course < numCourses; course++) {
            if (state[course] == UNVISITED) {
                if (hasCycle(graph, state, course, order)) {
                    return new int[0];  // Cycle! โŒ
                }
            }
        }

        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        // REVERSE POST-ORDER TO GET TOPOLOGICAL ORDER ๐Ÿ”„
        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        Collections.reverse(order);

        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        // CONVERT TO ARRAY ๐Ÿ“‹
        // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
        int[] result = new int[numCourses];
        for (int i = 0; i < numCourses; i++) {
            result[i] = order.get(i);
        }

        return result;
    }

    // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
    // DFS HELPER WITH POST-ORDER COLLECTION ๐Ÿ”
    // โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
    private boolean hasCycle(List<List<Integer>> graph, int[] state, 
                             int course, List<Integer> order) {
        // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
        // MARK AS VISITING ๐ŸŸก
        // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
        state[course] = VISITING;

        // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
        // EXPLORE NEIGHBORS ๐Ÿงญ
        // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
        for (int neighbor : graph.get(course)) {
            if (state[neighbor] == VISITING) {
                return true;  // Cycle! ๐Ÿ”ด
            }

            if (state[neighbor] == UNVISITED) {
                if (hasCycle(graph, state, neighbor, order)) {
                    return true;  // Cycle in subtree!
                }
            }
        }

        // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
        // MARK AS VISITED & ADD TO POST-ORDER ๐Ÿ“
        // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
        // All dependencies of this course are done!
        // Safe to add to ordering!
        // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
        state[course] = VISITED;
        order.add(course);  // Post-order collection! ๐Ÿ”‘

        return false;  // No cycle!
    }
}

Why This Works ๐ŸŽฏ

Post-order gives reverse topological order: ๐Ÿ“Š - When course finishes: all dependencies done - Add to list at this point - Results in reverse order!

Reversal gives correct order: ๐Ÿ”„ - Last finished = no dependencies โ†’ should be first! - First finished = most dependencies โ†’ should be last! - Reverse to get correct topological order!

Cycle detection same as before: โœ… - Three states work identically - VISITING โ†’ VISITING = cycle - Return empty if cycle detected!


๐Ÿ” Detailed Dry Run

Let's trace Approach 1 (BFS) on Example 2:

numCourses = 4
prerequisites = [[1,0], [2,0], [3,1], [3,2]]

โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
โ•‘                            SETUP                             โ•‘
โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

Build graph from prerequisites:
  [1,0]: 0 โ†’ 1
  [2,0]: 0 โ†’ 2
  [3,1]: 1 โ†’ 3
  [3,2]: 2 โ†’ 3

Adjacency list:
  0: [1, 2]
  1: [3]
  2: [3]
  3: []

Graph visualization:
       0
      / \
     โ†“   โ†“
     1   2
      \ /
       โ†“
       3

Calculate in-degrees:
  Course 0: 0 prerequisites โ†’ in-degree = 0
  Course 1: 1 prerequisite (0) โ†’ in-degree = 1
  Course 2: 1 prerequisite (0) โ†’ in-degree = 1
  Course 3: 2 prerequisites (1,2) โ†’ in-degree = 2

In-degree array: [0, 1, 1, 2]

BFS Processing ๐ŸŒŠ

โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
โ•‘ ๐Ÿ” INITIALIZATION                               โ•‘
โ• โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ฃ
โ•‘ Find courses with in-degree 0:                 โ•‘
โ•‘   Course 0: in-degree 0 โœ… Add to queue        โ•‘
โ•‘   Course 1: in-degree 1 โŒ                     โ•‘
โ•‘   Course 2: in-degree 1 โŒ                     โ•‘
โ•‘   Course 3: in-degree 2 โŒ                     โ•‘
โ•‘                                                 โ•‘
โ•‘ queue = [0]                                     โ•‘
โ•‘ order = []                                      โ•‘
โ•‘ index = 0                                       โ•‘
โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
โ•‘ ๐ŸŒŠ ITERATION 1: Process course 0               โ•‘
โ• โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ฃ
โ•‘ Poll: course 0                                  โ•‘
โ•‘                                                 โ•‘
โ•‘ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•‘
โ•‘ ADD TO RESULT ๐Ÿ“                               โ•‘
โ•‘ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•‘
โ•‘ order[0] = 0                                    โ•‘
โ•‘ order = [0, _, _, _]                           โ•‘
โ•‘ index = 1                                       โ•‘
โ•‘                                                 โ•‘
โ•‘ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•‘
โ•‘ UPDATE NEIGHBORS ๐Ÿ“‰                            โ•‘
โ•‘ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•‘
โ•‘ Course 0's neighbors: [1, 2]                   โ•‘
โ•‘                                                 โ•‘
โ•‘   Neighbor 1:                                   โ•‘
โ•‘     in-degree[1]-- โ†’ 1-1 = 0 โœ…               โ•‘
โ•‘     Now ready! Add to queue                     โ•‘
โ•‘                                                 โ•‘
โ•‘   Neighbor 2:                                   โ•‘
โ•‘     in-degree[2]-- โ†’ 1-1 = 0 โœ…               โ•‘
โ•‘     Now ready! Add to queue                     โ•‘
โ•‘                                                 โ•‘
โ•‘ queue = [1, 2]                                  โ•‘
โ•‘ in-degree = [0, 0, 0, 2]                       โ•‘
โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
โ•‘ ๐ŸŒŠ ITERATION 2: Process course 1               โ•‘
โ• โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ฃ
โ•‘ Poll: course 1                                  โ•‘
โ•‘                                                 โ•‘
โ•‘ ADD TO RESULT ๐Ÿ“                               โ•‘
โ•‘ order[1] = 1                                    โ•‘
โ•‘ order = [0, 1, _, _]                           โ•‘
โ•‘ index = 2                                       โ•‘
โ•‘                                                 โ•‘
โ•‘ UPDATE NEIGHBORS ๐Ÿ“‰                            โ•‘
โ•‘ Course 1's neighbors: [3]                      โ•‘
โ•‘                                                 โ•‘
โ•‘   Neighbor 3:                                   โ•‘
โ•‘     in-degree[3]-- โ†’ 2-1 = 1                   โ•‘
โ•‘     Still has dependency (course 2)            โ•‘
โ•‘     Don't add to queue yet                      โ•‘
โ•‘                                                 โ•‘
โ•‘ queue = [2]                                     โ•‘
โ•‘ in-degree = [0, 0, 0, 1]                       โ•‘
โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
โ•‘ ๐ŸŒŠ ITERATION 3: Process course 2               โ•‘
โ• โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ฃ
โ•‘ Poll: course 2                                  โ•‘
โ•‘                                                 โ•‘
โ•‘ ADD TO RESULT ๐Ÿ“                               โ•‘
โ•‘ order[2] = 2                                    โ•‘
โ•‘ order = [0, 1, 2, _]                           โ•‘
โ•‘ index = 3                                       โ•‘
โ•‘                                                 โ•‘
โ•‘ UPDATE NEIGHBORS ๐Ÿ“‰                            โ•‘
โ•‘ Course 2's neighbors: [3]                      โ•‘
โ•‘                                                 โ•‘
โ•‘   Neighbor 3:                                   โ•‘
โ•‘     in-degree[3]-- โ†’ 1-1 = 0 โœ…               โ•‘
โ•‘     Now ready! Add to queue                     โ•‘
โ•‘                                                 โ•‘
โ•‘ queue = [3]                                     โ•‘
โ•‘ in-degree = [0, 0, 0, 0]                       โ•‘
โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
โ•‘ ๐ŸŒŠ ITERATION 4: Process course 3               โ•‘
โ• โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ฃ
โ•‘ Poll: course 3                                  โ•‘
โ•‘                                                 โ•‘
โ•‘ ADD TO RESULT ๐Ÿ“                               โ•‘
โ•‘ order[3] = 3                                    โ•‘
โ•‘ order = [0, 1, 2, 3] โœ…                        โ•‘
โ•‘ index = 4                                       โ•‘
โ•‘                                                 โ•‘
โ•‘ UPDATE NEIGHBORS ๐Ÿ“‰                            โ•‘
โ•‘ Course 3's neighbors: [] (none)                โ•‘
โ•‘ No neighbors to update                          โ•‘
โ•‘                                                 โ•‘
โ•‘ queue = [] (empty!)                             โ•‘
โ•‘                                                 โ•‘
โ•‘ BFS Complete! โœ…                                โ•‘
โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

FINAL RESULT ๐ŸŽ‰

โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—
โ•‘         ๐ŸŽ‰ FINAL RESULT: [0,1,2,3]            โ•‘
โ• โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•ฃ
โ•‘                                               โ•‘
โ•‘  All courses processed! โœ…                    โ•‘
โ•‘  index = 4 = numCourses โœ…                    โ•‘
โ•‘                                               โ•‘
โ•‘  Valid topological ordering: [0, 1, 2, 3]    โ•‘
โ•‘                                               โ•‘
โ•‘  Verification:                                โ•‘
โ•‘    Course 0: No prerequisites โ†’ taken first  โ•‘
โ•‘    Course 1: Needs 0 โ†’ 0 before 1 โœ…         โ•‘
โ•‘    Course 2: Needs 0 โ†’ 0 before 2 โœ…         โ•‘
โ•‘    Course 3: Needs 1,2 โ†’ 1,2 before 3 โœ…     โ•‘
โ•‘                                               โ•‘
โ•‘  All prerequisites satisfied! ๐ŸŽ“              โ•‘
โ•‘                                               โ•‘
โ•‘  Note: [0,2,1,3] also valid!                 โ•‘
โ•‘  (1 and 2 can swap - both depend only on 0)  โ•‘
โ•‘                                               โ•‘
โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•

๐Ÿ”‘ Key Observations:

  1. Processing order IS the answer: ๐Ÿ“‹ No reversal needed with BFS!

  2. In-degrees track readiness: ๐Ÿ“Š Zero in-degree = ready to take

  3. Queue ensures correct order: ๐ŸŒŠ Dependencies satisfied before processing

  4. Index check detects cycles: ๐Ÿ” index < numCourses would mean cycle

  5. Clean and intuitive: โœจ BFS is perfect for this problem!


๐Ÿ“Š Complexity Analysis

Time Complexity: O(V + E) โšก

Where:
  V = numCourses (vertices)
  E = prerequisites.length (edges)

Both approaches (DFS and BFS):

Build graph: O(E)
  Process each prerequisite once

Approach 1 (BFS):
  Calculate in-degrees: O(E)
  BFS process each course once: O(V)
  Process each edge once: O(E)
  Total: O(V + E) โœ…

Approach 2 (DFS):
  DFS visit each course once: O(V)
  Explore each edge once: O(E)
  Reverse list: O(V)
  Total: O(V + E) โœ…

Both: O(V + E) - same as Course Schedule! โšก

Example: 2000 courses, 5000 prerequisites
  Time: 2000 + 5000 = 7000 operations
  Very efficient! ๐Ÿš€

Space Complexity: O(V + E) ๐Ÿ“ฆ

Space usage:

1. Graph (adjacency list): O(V + E) ๐Ÿ—๏ธ
   V lists, total E entries

2. Approach 1 (BFS):
   - In-degree array: O(V)
   - Queue: O(V) worst case
   - Result array: O(V)
   - Total: O(V + E) โœ…

3. Approach 2 (DFS):
   - State array: O(V)
   - Result list: O(V)
   - Recursion stack: O(V) worst case
   - Total: O(V + E) โœ…

Both: O(V + E) ๐Ÿ“ฆ

Same as Course Schedule!
Just added result array/list (O(V))
which doesn't change overall complexity!

Example: 2000 courses, 5000 prerequisites
  Graph: ~7000 space
  Additional: ~2000-4000 space
  Total: ~11000 space units
  Reasonable! โœ…

Core Pattern: Topological Sort with Output ๐Ÿ“‹

// Template for topological sort with ordering
class Solution {
    // BFS approach (Kahn's) - RECOMMENDED for output
    public int[] topologicalSortBFS(int n, int[][] edges) {
        List<List<Integer>> graph = buildGraph(n, edges);
        int[] inDegree = calculateInDegrees(n, edges);

        Queue<Integer> queue = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            if (inDegree[i] == 0) queue.offer(i);
        }

        int[] result = new int[n];
        int index = 0;

        while (!queue.isEmpty()) {
            int node = queue.poll();
            result[index++] = node;  // Collect order! ๐Ÿ”‘

            for (int neighbor : graph.get(node)) {
                if (--inDegree[neighbor] == 0) {
                    queue.offer(neighbor);
                }
            }
        }

        return index == n ? result : new int[0];
    }

    // DFS approach - needs reversal
    public int[] topologicalSortDFS(int n, int[][] edges) {
        List<List<Integer>> graph = buildGraph(n, edges);
        int[] state = new int[n];
        List<Integer> order = new ArrayList<>();

        for (int i = 0; i < n; i++) {
            if (state[i] == UNVISITED) {
                if (hasCycle(graph, state, i, order)) {
                    return new int[0];
                }
            }
        }

        Collections.reverse(order);  // Reverse post-order! ๐Ÿ”„
        return order.stream().mapToInt(Integer::intValue).toArray();
    }
}

When to Use This Pattern ๐ŸŽฏ

โœ… Need actual topological ordering (not just existence)
โœ… Return one valid order (not all)
โœ… Dependencies between items
โœ… Cycle detection + ordering
โœ… Prerequisite-based problems

Recognition triggers: ๐Ÿ” - "Return the ordering" or "return order" ๐Ÿ“‹ - "One valid order" (any valid answer) โœจ - Same as Course Schedule but need ordering ๐Ÿ“Š - Topological sort with output ๐ŸŽฏ

Key insight: BFS (Kahn's) cleaner for output problems! ๐Ÿ’ก

Direct Applications:

  1. Course Schedule (LC 207) - Medium ๐Ÿ“š โ€ข What's similar: Same graph, same cycle detection ๐Ÿ”„ โ€ข What's different: Boolean output, not ordering โœ…/โŒ โ€ข Key insight: Prerequisite problem! ๐ŸŽฏ

  2. Alien Dictionary (LC 269) - Hard ๐Ÿ‘ฝ โ€ข What's similar: Topological sort with output ๐Ÿ“‹ โ€ข What's different: Build graph from word comparison ๐Ÿ” โ€ข Key insight: Extract dependencies first! ๐Ÿ’ก

  3. Parallel Courses II (LC 1494) - Hard ๐ŸŽ“ โ€ข What's similar: Course dependencies, topological sort ๐Ÿ“š โ€ข What's different: Minimize semesters, k courses/semester ๐Ÿ“Š โ€ข Key insight: Topological levels + optimization! โšก

  4. Sequence Reconstruction (LC 444) - Medium ๐Ÿ”ข โ€ข What's similar: Topological sort, unique ordering ๐ŸŽฏ โ€ข What's different: Verify sequences give unique order โœ… โ€ข Key insight: Check uniqueness during Kahn's! ๐Ÿ”‘

Pattern Progression: ๐Ÿ“ˆ

Level 1: Detect Ordering Exists โœ…/โŒ
  โ””โ”€ Course Schedule (LC 207)

Level 2: Return One Valid Ordering ๐Ÿ“‹ โ† HERE ๐ŸŽฏ
  โ”œโ”€ Course Schedule II (LC 210)
  โ”œโ”€ Alien Dictionary (LC 269)
  โ””โ”€ Minimum Height Trees (LC 310)

Level 3: Advanced Topological Sort ๐ŸŒŸ
  โ”œโ”€ Parallel Courses II (LC 1494)
  โ”œโ”€ Sequence Reconstruction (LC 444)
  โ””โ”€ Sort Items by Groups (LC 1203)

๐Ÿ“ Quick Revision Notes

๐ŸŽฏ Core Concept

Course Schedule II returns actual topological ordering using BFS (Kahn's) or DFS ๐Ÿ“‹. BFS is CLEANER: processing order IS answer (no reversal!) ๐ŸŒŠ. Calculate in-degrees, queue zero in-degree courses ๐Ÿ“Š. Process: add to result, decrease neighbors' in-degrees, queue newly-zero โœ…. If processed all, return order ๐ŸŽ“. Otherwise empty array (cycle) ๐Ÿ”„. DFS works but needs post-order collection + reversal ๐Ÿ”ƒ. Time: O(V+E) โšก, Space: O(V+E) ๐Ÿ“ฆ. BFS recommended! โญ

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

public class Solution {
  public int[] findOrder(int numCourses, int[][] prerequisites) {
    List<Integer> res = new ArrayList<>();

    // Approach 1 - BFS (kahn)
    res = bfs(numCourses, prerequisites, res); // GOTCHA
    Collections.reverse(res);

    // // Approach 2 - DFS (plus stack)
    // if (dfs(numCourses, prerequisites, res)) { // GOTCHA
    // return new int[0];
    // }

    return res.stream().mapToInt(i -> i).toArray();
  }

  private boolean dfs(int numCourses, int[][] prerequisites, List<Integer> res) {
    // Step 1: create graph (list of list) - no indegrees
    ArrayList<ArrayList<Integer>> graph = new ArrayList<>();

    for (int i = 0; i < numCourses; i++) {
      graph.add(new ArrayList<>());
    }

    for (int i = 0; i < prerequisites.length; i++) {
      graph.get(prerequisites[i][0]).add(prerequisites[i][1]);
    }

    // State 2: Create and track status of nodes (0, 1, 2) with 0 being default.
    // 0 - UNVISITED, 1 - VISITING and 2 - VISITED
    int[] state = new int[numCourses];

    // State 3: Loop every course and detch to find cycle.
    // If loop found, return false. Else, return true.
    for (int i = 0; i < numCourses; i++) {
      if (state[i] == 0) {
        if (dfsUtil(i, state, graph, res)) { // has cycle?
          return true;
        }
      }
    }

    return false;
  }

  private boolean dfsUtil(int course, int[] state, ArrayList<ArrayList<Integer>> graph, List<Integer> res) {
    // Step 4: change the state of course being visited or considered.
    state[course] = 1;

    for (int depCourse : graph.get(course)) {
      // Step 5: check the state of dependent course.
      // If cycle is there, it gets detected here.
      if (state[depCourse] == 1) {
        return true;
      }

      // Prevents infinite loop (acts as visited)
      if (state[depCourse] == 0) {
        // Else, do a DFS from here.
        if (dfsUtil(depCourse, state, graph, res)) {
          return true;
        }
      }
    }

    // Step 6: Mark the node as visited
    state[course] = 2;
    res.add(course); // course added to res

    return false;
  }

  private List<Integer> bfs(int numCourses, int[][] prerequisites, List<Integer> res) {
    // Step 1: create graph (list of list) and indegrees (array).
    ArrayList<ArrayList<Integer>> graph = new ArrayList<>();
    int[] indegrees = new int[numCourses];

    for (int i = 0; i < numCourses; i++) {
      graph.add(new ArrayList<>());
    }

    for (int i = 0; i < prerequisites.length; i++) {
      graph.get(prerequisites[i][0]).add(prerequisites[i][1]);
      indegrees[prerequisites[i][1]]++;
    }

    // Step 2: find courses with no dependencies (indegrees with 0 value)
    Queue<Integer> queue = new LinkedList<>();
    for (int i = 0; i < numCourses; i++) {
      if (indegrees[i] == 0) {
        queue.offer(i);
      }
    }

    // Step 3: explore queue
    int coursesTaken = 0;
    while (!queue.isEmpty()) {
      int course = queue.poll();
      res.add(course); // course added to res
      coursesTaken++; // course taken

      for (int depCourse : graph.get(course)) {
        indegrees[depCourse]--;

        if (indegrees[depCourse] == 0) { // no dependancy anymore
          queue.offer(depCourse);
        }
      }
    }

    return coursesTaken == numCourses ? res : new ArrayList<>();
  }

  public static void main(String[] args) {
    Solution s = new Solution();
    System.out.println(Arrays.toString(s.findOrder(2, new int[][] { { 1, 0 } })));
    System.out.println(Arrays.toString(s.findOrder(4, new int[][] { { 1, 0 }, { 2, 0 }, { 3, 1 }, { 3, 2 } })));
    System.out.println(Arrays.toString(s.findOrder(1, new int[][] {})));
    System.out.println(Arrays.toString(s.findOrder(3, new int[][] { { 0, 2 }, { 1, 2 }, { 2, 0 } })));
  }
}

๐Ÿ”‘ Key Insights

BFS vs DFS for Ordering: ๐Ÿ†

BFS (Kahn's Algorithm): โญ RECOMMENDED

Processing order = topological order!
  while (queue not empty):
    course = queue.poll()
    result[index++] = course  // Add directly! โœ…

No reversal needed! ๐ŸŽฏ

Advantages:
  โœ… Cleaner code
  โœ… More intuitive
  โœ… No reversal step
  โœ… Direct ordering

DFS (Post-order):

Post-order = reverse topological order!
  void dfs(node):
    // ... explore neighbors ...
    result.add(node)  // Add in post-order

  Collections.reverse(result)  // Must reverse! ๐Ÿ”„

Disadvantages:
  โŒ Need reversal step
  โŒ Less intuitive
  โŒ Post-order concept confusing

For ordering problems: USE BFS! ๐ŸŽฏ

Why Processing Order Works: ๐Ÿ’ก

Kahn's algorithm processes in dependency order:

Step 1: Process nodes with in-degree 0
  - These have NO dependencies
  - Safe to process first! โœ…

Step 2: Remove processed nodes
  - Decrease neighbors' in-degrees
  - Find NEW zero in-degree nodes

Step 3: Process newly-ready nodes
  - Their dependencies now satisfied
  - Safe to process! โœ…

Step 4: Repeat until done

The order we process = valid topological order!

Example:
  Graph: A โ†’ B โ†’ C

  Round 1: Process A (in-degree 0)
           order = [A]
           B now in-degree 0

  Round 2: Process B
           order = [A, B]
           C now in-degree 0

  Round 3: Process C
           order = [A, B, C] โœ…

Processing order respects Aโ†’Bโ†’C! ๐ŸŽฏ

Multiple Valid Orders: โœจ

Many problems have multiple valid topological orders!

Example:
       A
      / \
     B   C
      \ /
       D

Valid orders:
  [A, B, C, D] โœ…
  [A, C, B, D] โœ…

Why? B and C both depend only on A
     Can take in either order!

The problem: "Return ANY valid order"
  Don't need all orders
  Don't need specific order
  Just ONE that works! ๐ŸŽฏ

Our BFS returns one valid order
  (Based on queue processing order)
  That's perfect! โœ…

Empty Array for Cycles: ๐Ÿ”„

Key difference from Course Schedule:

Course Schedule: return false (boolean)
Course Schedule II: return [] (empty array)

Why empty array signals cycle:
  If cycle exists:
    Some courses never reach in-degree 0
    Never added to queue
    Never processed
    index < numCourses

  Return new int[0] (empty array)

Clean way to signal "impossible"! ๐Ÿ“‹

Check at end:
  return index == numCourses ? order : new int[0];

Simple and elegant! โœจ

๐ŸŽช Memory Aid

"BFS gives order as we go" ๐ŸŒŠ
"No reversal needed, smooth flow" โšก
"DFS collects then must reverse the show" ๐Ÿ”„
"For ordering problems, BFS is pro" ๐Ÿ† โœจ

โš ๏ธ Don't Forget

  • Use BFS for ordering - cleaner than DFS! โญ
  • Processing order IS answer - no reversal needed! ๐ŸŒŠ
  • Add to result while processing - not after! ๐Ÿ“
  • Check index == numCourses - detects cycles! ๐Ÿ”
  • Return empty array on cycle - not null! ๐Ÿ“‹
  • Multiple valid orders exist - return any one! โœจ

You've mastered Course Schedule II - topological sort with output! ๐ŸŒŸ๐Ÿ“šโœจ