Pattern #55

Kahn's Algorithm

Important interview questions, thinking patterns, Indegree 0 Ready Queue, Cycle Deadlock validation, Min Heap Topo variants, Parallel Semesters, and Go templates.

Must Solve

15 core questions — solve these first.

  1. 1.Course Schedule
    medium
  2. 2.Course Schedule II
    medium
  3. 3.Topological Sort
    medium
  4. 4.Alien Dictionary
    hard
  5. 5.Parallel Courses
    medium
  6. 6.Parallel Courses III
    hard
  7. 7.Find All Possible Recipes From Given Supplies
    medium
  8. 8.Sequence Reconstruction
    medium
  9. 9.Minimum Semesters to Finish Courses
    medium
  10. 10.Build Order
    medium
  11. 11.Task Scheduling with Dependencies
    medium
  12. 12.Largest Color Value in a Directed Graph
    hard
  13. 13.Sort Items by Groups Respecting Dependencies
    hard
  14. 14.Find Eventual Safe States
    medium
  15. 15.Detect Cycle in Directed Graph Using Kahn
    medium

Also Important

8 more questions worth practicing.

  1. 16.Minimum Time to Complete All Tasks
    medium
  2. 17.Critical Path in DAG
    hard
  3. 18.DAG Layer Processing
    medium
  4. 19.Package Installation Order
    medium
  5. 20.Workflow Dependency Execution
    medium
  6. 21.Dependency Resolution
    medium
  7. 22.Lexicographically Smallest Topological Order
    medium
  8. 23.Unique Topological Ordering
    medium

How to Think

  1. Need dependency order?Kahn's Algorithm (Indegree 0 Queue)
  2. Need nodes that are ready now?Indegree == 0 (0 unfinished prerequisites)
  3. Need detect cycle in directed graph?Kahn fails (processedCount < totalNodes)
  4. Need process dependency levels / semesters?Kahn + BFS Level loop (size := len(queue))

Go Kahn's Algorithm Code Templates

Standard Kahn Queue, Parallel Semester Levels & Lexicographically Smallest Min Heap in Go

// 1. Standard Kahn's Algorithm (BFS Queue): O(V + E) Time
func topoSortKahn(n int, edges [][]int) []int {
    adj := make([][]int, n)
    indegree := make([]int, n)

    for _, e := range edges {
        u, v := e[0], e[1]
        adj[u] = append(adj[u], v)
        indegree[v]++
    }

    queue := []int{}
    for i := 0; i < n; i++ {
        if indegree[i] == 0 {
            queue = append(queue, i)
        }
    }

    order := []int{}
    head := 0

    for head < len(queue) {
        u := queue[head]
        head++
        order = append(order, u)

        for _, v := range adj[u] {
            indegree[v]--
            if indegree[v] == 0 {
                queue = append(queue, v) // Unlocked!
            }
        }
    }

    if len(order) != n {
        return nil // Cycle detected!
    }
    return order
}

// 2. Parallel Courses / Semesters (Level Loop Kahn): O(V + E) Time
func minSemesters(n int, relations [][]int) int {
    adj := make([][]int, n+1)
    indegree := make([]int, n+1)

    for _, r := range relations {
        adj[r[0]] = append(adj[r[0]], r[1])
        indegree[r[1]]++
    }

    queue := []int{}
    for i := 1; i <= n; i++ {
        if indegree[i] == 0 { queue = append(queue, i) }
    }

    semesters := 0
    processed := 0

    for len(queue) > 0 {
        size := len(queue)
        semesters++
        for i := 0; i < size; i++ {
            u := queue[0]
            queue = queue[1:]
            processed++

            for _, v := range adj[u] {
                indegree[v]--
                if indegree[v] == 0 {
                    queue = append(queue, v)
                }
            }
        }
    }

    if processed != n { return -1 }
    return semesters
}

👉 Total Time: O(V + E) | Space: O(V + E) graph + indegrees + queue

The Indegree Unlocking Rule

1. Indegree Mental Model: Indegree represents the number of unfinished prerequisites pointing into a node.

2. Zero Prerequisites = Ready: Nodes with indegree == 0 have no incoming dependencies blocking them and can enter the ready queue immediately.

3. Unlocking Neighbors: When node u is processed, decrement indegree[v]-- for all outgoing edges u -> v. As soon as indegree[v] == 0, node v becomes unlocked and is enqueued!

Parallel Execution Waves & Min Heap Topo

1. Parallel Execution Waves: In task scheduling or semester requirements, wrapping the queue pop in a level loop (size := len(queue)) processes all currently ready tasks in parallel in 1 round.

2. Lexicographically Smallest Order: When an interview requires the smallest possible valid sequence, replace the FIFO Queue with a Min Heap (PriorityQueue). Whenever multiple nodes are ready, the min heap picks the smallest node.

Visual Memory Rule
Kahn's Algorithm   → Indegree 0 Ready Queue for DAG topological ordering
Indegree           → Number of unfinished prerequisites pointing to node
Queue Pop          → Append to order -> Decrement neighbors' indegree
Neighbor 0         → Enqueue immediately (Unlocked!)
Cycle Check        → processedCount < n when queue empty = Dependency Deadlock!
Min Heap Variant   → Replace Queue with Min Heap for smallest numerical order

💡 Golden Rule: "Keep processing whatever has zero unfinished dependencies; each completed node may unlock the next set of ready nodes."

Common Interview Mistakes

1. Reversing Edge Direction

If "A depends on B", the edge MUST be B -> A (B before A). Reversing direction corrupts indegree counts!

2. Decreasing Wrong Node's Indegree

When processing node u with edge u -> v, decrement indegree[v]--, NOT u.

3. Enqueueing Neighbor Before It Reaches Zero

Enqueue a neighbor ONLY when its indegree drops to PRECISELY 0 (indegree[v] == 0).

4. Forgetting Isolated Nodes

Nodes with zero incoming and outgoing edges still have indegree == 0 and belong in the topological order!

Interview Rules

  1. 1. Prerequisites / Dependencies?→ Kahn's Algorithm (Indegree 0 Queue)
  2. 2. Indegree calculation? → Count incoming edges for each node 0..n-1
  3. 3. Processing step? → Pop ready node u, append to order, decrement indegree[v]-- for all u -> v
  4. 4. Neighbor unlocking? → Enqueue neighbor v immediately when indegree[v] == 0
  5. 5. Dependency cycle check? → Cycle exists if processedCount < n when queue empties
  6. 6. Parallel rounds / semesters? → Level-by-level queue size loop (size := len(queue))
  7. 7. Smallest valid order? → Replace Queue with Min Heap (PriorityQueue)
  8. 8. Overall Complexity → Time: O(V + E) | Space: O(V + E)

Small Rules

  1. Rule 1:Kahn's algorithm models tasks in two states: Blocked (indegree > 0) and Ready (indegree == 0).
  2. Rule 2: Completing a ready node decreases the indegree of all its outgoing dependencies.
  3. Rule 3: Neighbors are enqueued only when their indegree drops to 0.
  4. Rule 4: Queue emptying before processing all nodes proves a directed cycle deadlock.
  5. Rule 5: Level-order Kahn loops calculate parallel execution rounds.

Production Thinking

Job Scheduler Ready QueueDispatching unblocked tasks (indegree 0) to worker thread pools

CI/CD Pipeline ExecutionExecuting build, lint, test, and deploy stages in valid dependency waves

Package Installation ResolutionInstalling prerequisite libraries before dependent software packages

Workflow Engine OrchestrationTracking remaining prerequisite counts for state machine transitions

Remember This

Kahn's Algorithm → Indegree 0 Queue

Build Graph + Calculate Indegrees
Indegree 0       → Ready Queue
Pop Ready Node   → Add to Order
For Each Neighbor→ indegree--
Becomes 0?       → Enqueue (Unlocked!)
Queue Empty      → Check processedCount == N (Cycle if < N)
Parallel Rounds  → Level loop (size := len(queue))
Smallest Order   → Min Heap (PriorityQueue)
Time             → O(V + E)

💡 Golden Rule: "Keep processing whatever has zero unfinished dependencies; each completed node may unlock the next set of ready nodes."