一、拓扑排序
1.1 定义
对有向无环图(DAG)的顶点进行线性排序,使得对每条边 ((u, v)),u在v之前。
1.2 应用场景
- 任务调度(先修课程)
- 编译顺序
- Makefile依赖
- 数据处理流水线
二、Kahn算法(BFS)
2.1 核心思想
反复选择入度为0的顶点,移除其出边。
2.2 实现
public List<Integer> topologicalSort(int n, List<Integer>[] adj) { int[] inDegree = new int[n]; for (List<Integer> edges : adj) { for (int v : edges) inDegree[v]++; } Queue<Integer> queue = new LinkedList<>(); for (int i = 0; i < n; i++) { if (inDegree[i] == 0) queue.offer(i); } List<Integer> result = new ArrayList<>(); while (!queue.isEmpty()) { int u = queue.poll(); result.add(u); for (int v : adj[u]) { if (--inDegree[v] == 0) queue.offer(v); } } return result.size() == n ? result : new ArrayList<>(); }
|
2.3 复杂度
( O(V + E) )
三、DFS实现
public List<Integer> topologicalSortDFS(int n, List<Integer>[] adj) { boolean[] visited = new boolean[n]; boolean[] onStack = new boolean[n]; Deque<Integer> stack = new ArrayDeque<>(); for (int i = 0; i < n; i++) { if (!visited[i] && !dfs(i, adj, visited, onStack, stack)) { return new ArrayList<>(); } } List<Integer> result = new ArrayList<>(); while (!stack.isEmpty()) result.add(stack.pop()); return result; }
boolean dfs(int u, List<Integer>[] adj, boolean[] visited, boolean[] onStack, Deque<Integer> stack) { visited[u] = true; onStack[u] = true; for (int v : adj[u]) { if (onStack[v]) return false; if (!visited[v] && !dfs(v, adj, visited, onStack, stack)) return false; } onStack[u] = false; stack.push(u); return true; }
|
四、环检测
拓扑排序可以检测有向图是否有环:
- Kahn算法:结果顶点数 < 总顶点数
- DFS:遇到回边(onStack中的节点)
五、总结
| 算法 |
方式 |
特点 |
| Kahn |
BFS |
直观,适合找所有拓扑序 |
| DFS |
递归 |
简洁,可顺便检测环 |
拓扑排序是处理依赖关系的有力工具。只要问题中存在”A必须在B之前”的约束,就可以考虑使用拓扑排序。