拓扑排序

推荐读本《图算法》

拓扑排序

拓扑排序是一种应用广泛的图算法,通常见于任务DAG图的执行排序,由于任务图有依赖,必须优先执行上游任务再逐步解锁下游任务,因此执行顺序至关重要。
拓扑排序主要思想是:

  • 访问入度为0的点(访问没有上游任务依赖的节点)
  • 将下游的依赖点入度减1(下游任务检测到一个依赖任务已经完成)
  • 循环前面两个步骤,直到所有点都被访问

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public static class Node {
List<Node> children;
}
public static List<Node> topsort(List<Node> nodes) {
List<Node> result = new ArrayList<Node>();//已经排好序的列表
Map<Node, Integer> num_parent = new HashMap<Node, Integer>();//上游点的个数
for (Node node : nodes) {
if (!num_parent.containsKey(node)) {
num_parent.put(node, 0);
}
for (Node child : node.children) {
//对每个下游,累加,如果node数据结构有指向上游节点的引用,就不用这么计算
num_parent.compute(child, new BiFunction<Node, Integer, Integer>() {
public Integer apply(Node node, Integer number) {
if (number == null) {
return 1;
} else {
return number + 1;
}
}
});
}
}
Queue<Node> ready = new ArrayDeque<Node>();
for (Node node : nodes) {
//第一轮没有上游节点的节点,可以优先计算
if (num_parent.get(node) == 0) {
ready.add(node);
}
}
while (!ready.isEmpty()) {
//execute(ready); 这里可以多个节点一起取,这些节点是没有互相之间的依赖,可以以任意顺序访问
Node current = ready.poll();
result.add(current);
for (Node child : current.children) {
num_parent.compute(child, new BiFunction<Node, Integer, Integer>() {
public Integer apply(Node node, Integer number) {
return number - 1;
}
});
if (num_parent.get(child) == 0) {
ready.add(child);
}
}
}
return result;
}