Topological Order (Kahn's Algorithm)

Problem

The first line has n and m: the number of tasks (0 to n-1) and the number of rules. Each of the next m lines has a b: task a must be done before task b. Print an order that follows all rules. If more than one order is possible, print the one that is smallest when compared number by number. If the rules contain a cycle, print cycle.

Constraints

  • 1 <= n <= 1000
  • 0 <= m <= 5000

Examples

Input

4 3
0 1
1 2
0 3

Output

0 1 2 3

Solutions

Best approachTime O((n + m) log n)Space O(n + m)

Kahn's algorithm repeatedly takes a task with no unfinished prerequisite. Using a min-heap makes the choice among ready tasks the smallest number. If the output has fewer than n tasks, the rest are in a cycle.

Same code works in Java810172125Basic features only, so nothing changes between versions.
/*
 * Problem : Topological order.
 * Input   : n and m, then m rule lines.
 * Output  : The order or cycle.
 * Approach: Kahn's algorithm with a heap.
 * Time    : O((n + m) log n)
 * Space   : O(n + m)
 * Java    : 8 or newer.
 */
import java.util.Scanner;

public class Main {
    // This method solves the task. It receives the input and returns the answer.
    static String solve(String[] lines) {
        String[] header = lines[0].split(" ");
        int n = Integer.parseInt(header[0]);

        // Step 1: Build the graph. indegree[v] = how many edges point to v.
        java.util.List<java.util.List<Integer>> next = new java.util.ArrayList<>();
        int[] indegree = new int[n];
        for (int i = 0; i < n; i++) {
            next.add(new java.util.ArrayList<>());
        }
        for (int i = 1; i < lines.length; i++) {
            String[] edge = lines[i].split(" ");
            int from = Integer.parseInt(edge[0]);
            int to = Integer.parseInt(edge[1]);
            next.get(from).add(to);
            indegree[to]++;
        }

        // Step 2: A PriorityQueue always gives the smallest ready node. Ready means: no unfinished edge points to it.
        java.util.PriorityQueue<Integer> ready = new java.util.PriorityQueue<>();
        for (int i = 0; i < n; i++) {
            if (indegree[i] == 0) {
                ready.add(i);
            }
        }

        // Step 3: Take a ready node, put it in the order and remove its edges. New ready nodes join the queue.
        StringBuilder order = new StringBuilder();
        int taken = 0;
        while (!ready.isEmpty()) {
            int node = ready.poll();
            order.append(taken == 0 ? "" : " ").append(node);
            taken++;
            for (int target : next.get(node)) {
                indegree[target]--;
                if (indegree[target] == 0) {
                    ready.add(target);
                }
            }
        }

        // Step 4: If some nodes were never ready, there is a cycle and no valid order exists.
        return taken == n ? order.toString() : "cycle";
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        java.util.List<String> all = new java.util.ArrayList<>();
        while (scanner.hasNextLine()) {
            String line = scanner.nextLine().trim();
            if (!line.isEmpty()) {
                all.add(line);
            }
        }
        String[] lines = all.toArray(new String[0]);
        System.out.println(solve(lines));
    }
}
Hints (1)
  1. What does it mean if some tasks are never ready?