Back to Arrays
Q2Question 2 of 109

Kahn's Algorithm (BFS topological sort): in-degree array + zero-degree queue

Key Concepts
  • Kahn's algorithm produces a topological order by repeatedly removing nodes with zero in-degree.
  • Maintain an in-degree count per node and a queue of nodes whose in-degree has reached zero.
  • As each zero-degree node is emitted, decrement successors' in-degrees and enqueue any that reach zero.
  • If fewer than all nodes are emitted, the graph contains a cycle and no valid order exists.
  • Time is O(V + E) and space is O(V + E) for the adjacency list, in-degree array, and queue.
  • Replacing the queue with a priority queue yields a specific ordering such as lexicographically smallest.

Definition

Repeatedly emit zero-in-degree nodes and decrement their successors; the emission order is a topological sort, and a short count means a cycle.
  • In-degree tracking – count incoming edges for each node; nodes with zero in-degree have no pending dependencies.
  • Queue of ready nodes – enqueue all zero-in-degree nodes as the starting batch.
  • Emit and update – dequeue a node, append it to the order, and decrement successors' in-degrees.
  • Cycle detection – if the emitted count is less than the total node count, a cycle exists.
  • O(V + E) time – each node is enqueued once and each edge is processed once.

Think of sequencing end-of-day settlement tasks at National Bank of India where some tasks must precede others. Pankaj starts with tasks that have no pending predecessor, completes them, and each completion removes a dependency from waiting tasks, unlocking new ready tasks. The order he completes them is a valid schedule, and if some tasks never become ready there is a circular dependency.

Understanding the Concepts

What is topological sorting?

Topological sorting produces a linear ordering of nodes in a directed acyclic graph (DAG) such that for every directed edge u to v, node u appears before node v. In a banking context, Pankaj manages end-of-day settlement tasks at National Bank of India where some tasks depend on others, and he needs a valid execution order.

How does Kahn's algorithm work?

Build the adjacency list and compute each node's in-degree. Enqueue every node whose in-degree is zero. Repeatedly dequeue a node, append it to the order, and for each successor decrement its in-degree, enqueueing it when the in-degree hits zero.

JAVA
package com.bank.service;

import java.util.*;

public class SettlementScheduler {

    public static List<Integer> topologicalSort(int numTasks, int[][] dependencies) {
        List<List<Integer>> adj = new ArrayList<>();
        int[] inDegree = new int[numTasks];
        for (int i = 0; i < numTasks; i++) adj.add(new ArrayList<>());

        for (int[] dep : dependencies) {
            adj.get(dep[0]).add(dep[1]);
            inDegree[dep[1]]++;
        }

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

        List<Integer> order = new ArrayList<>();
        while (!queue.isEmpty()) {
            int task = queue.poll();
            order.add(task);
            for (int next : adj.get(task)) {
                inDegree[next]--;
                if (inDegree[next] == 0) queue.add(next);
            }
        }

        if (order.size() != numTasks) {
            throw new IllegalStateException("Cycle detected: no valid schedule");
        }
        return order;
    }

    public static void main(String[] args) {
        // Tasks: 0=Load accounts, 1=Compute interest, 2=Apply fees,
        //        3=Generate statements, 4=Send notifications
        int[][] deps = {{0,1}, {0,2}, {1,3}, {2,3}, {3,4}};
        List<Integer> schedule = topologicalSort(5, deps);
        String[] names = {"Load accounts", "Compute interest",
            "Apply fees", "Generate statements", "Send notifications"};
        System.out.println("Settlement schedule:");
        for (int task : schedule) {
            System.out.println("  " + task + ": " + names[task]);
        }
    }
}
TEXT
Settlement schedule:
  0: Load accounts
  1: Compute interest
  2: Apply fees
  3: Generate statements
  4: Send notifications

How does cycle detection work?

If the graph has a cycle, some nodes never reach zero in-degree because they are locked in a circular dependency. The emitted count will be less than the total node count. This check comes for free from the algorithm.

What is the time and space complexity?

  • Time: O(V + E) because each node is enqueued once and each edge is processed once.
  • Space: O(V + E) for the adjacency list, in-degree array, queue, and output list.

How is Kahn's different from DFS-based topological sort?

| Aspect | Kahn's (BFS) | DFS-based |
|---|---|---|
| Approach | Remove zero-in-degree nodes | Post-order reversal |
| Cycle detection | Emitted count < total | Gray node re-visited |
| Iterative | Naturally iterative | Needs explicit stack for large graphs |
| Lex-smallest order | Use PriorityQueue | Not straightforward |

How do you get the lexicographically smallest order?

Replace the plain Queue with a PriorityQueue so that among nodes with zero in-degree, the smallest one is always chosen first.

JAVA
Queue<Integer> queue = new PriorityQueue<>();
// Rest of algorithm unchanged

What are common mistakes?

The most common mistake is forgetting to initialise the in-degree array, leading to all nodes being enqueued immediately. Another is not checking the emitted count for cycle detection.

JAVA
// Wrong: missing cycle check
return order; // may return partial order if cycle exists

// Correct: verify all nodes were emitted
if (order.size() != numTasks) {
    throw new IllegalStateException("Cycle detected");
}

What should a senior engineer know?

A senior engineer knows that Kahn's algorithm is naturally iterative, avoiding the recursion-depth limits a DFS-based sort can hit on large graphs. The senior also knows that the algorithm is the foundation for build systems (Maven, Gradle) that resolve dependency order, and for database migration tools that order schema changes. In the settlement pipeline at National Bank of India, Pankaj uses this to produce a deterministic execution order for hundreds of interdependent tasks.

Visual Overview

DIAGRAM
  Dependencies:
  0 (Load) ──→ 1 (Interest)
  0 (Load) ──→ 2 (Fees)
  1 (Interest) ──→ 3 (Statements)
  2 (Fees) ──→ 3 (Statements)
  3 (Statements) ──→ 4 (Notify)

  In-degrees: [0]=0  [1]=1  [2]=1  [3]=2  [4]=1

  Queue: [0]  → emit 0, decrement 1,2
  In-degrees: [1]=0  [2]=0  [3]=2  [4]=1
  Queue: [1,2] → emit 1, decrement 3
  Queue: [2]   → emit 2, decrement 3
  In-degrees: [3]=0  [4]=1
  Queue: [3]   → emit 3, decrement 4
  Queue: [4]   → emit 4
  Order: [0, 1, 2, 3, 4]  (all 5 emitted, no cycle)

Key Takeaway

Kahn's algorithm produces a topological order of a directed acyclic graph by repeatedly removing nodes with no incoming edges. I compute each node's in-degree, enqueue every zero-in-degree node, then repeatedly dequeue, emit, and decrement successors' in-degrees, enqueueing any that reach zero.

When the queue empties, if I have emitted every node the order is valid. If fewer were emitted, a cycle exists. This cycle detection comes for free from the emitted-count check.

In my banking project at National Bank of India, end-of-day settlement tasks had precedence constraints and needed a valid execution order. Kahn's algorithm emitted each task as its predecessors completed, producing a runnable schedule.

The senior points are that Kahn's is naturally iterative, avoiding recursion-depth limits, and that swapping the queue for a PriorityQueue yields a specific order such as lexicographically smallest.

Remember one sentence: enqueue zero-in-degree nodes, emit and decrement successors, and a short count means a cycle.

---

Share
Use ← → arrow keys to navigate