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
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.
/*
* 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)
- What does it mean if some tasks are never ready?