拓扑排序
1. 定义
拓扑排序(Topological Sorting)是一个有向无环图(DAG, Directed Acyclic Graph)的所有顶点的线性序列。且该序列必须满足下面两个条件:
-
每个顶点出现且只出现一次。
-
若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。
2. 应用
- Build Systems:
- 确定library的built顺序
- Advanced-packaging tool (apt-get):
- Sort the list of packages to be installed or removed
- Task scheduling:
- Schedule inter-dependent tasks
- Pre-requisite problems:
- University course schedule
3. 实现原理
一般拓扑排序有两种不同的实现方法,贪心算法和DFS。
3.1 贪心算法
图示:
算法概述:
- 从DAG图中选择indegree为0的点并输出
- 从图中删除该点以及所有以他为起点的边
- 重复前两个操作直到DAG图为空或者图中不存在indegree为0的node为止
环路检测:
- 如果DAG图不为空,但是已经不存在indegree为0的node,说明必然有环存在
PseudoCode:
Greedy-Based-Topological-Sorting(edges):
indegree = new dict
graph = new dict
stack = new stack
result = new array/list
for edge in edges:
add vertex relationship to graph
update vertex indegree
for v in all vertex:
if indegree[v] == 0:
stack.append(v)
while stack is not empty:
v = stack.pop()
result.append(v)
for adj in all adjacent of v:
indegree[adj] -= 1
if indegree[adj] == 0:
stack.append(adj)
return result
时间复杂度:
Θ(V+E),V表示顶点的个数,E表示边的个数。
求各个顶点的indegree是O(E), 把顶点入栈是O(V)
Sample Code: here
2.2 用户DFS实现
深度优先搜索过程中,当到达出度为0的顶点时,需要进行回退。在执行回退时记录出度为0的顶点,将其入栈。则最终出栈顺序的逆序即为拓扑排序序列。
视频讲解:
参考Geeks的视频,Here.
算法概述:
- 对graph进行dfs搜索
- 如果某个vertex无adj,那么加入result中
- 最后result.reverse()就是结果
环路检测:
在单个vertex的dfs recursion中,如果一个vertex被访问多次,说明有回路
伪代码:
Topological-DFS-Helper(v, visited, stack):
visited[v] = true
for i in all adj of v:
if visited[i] == false:
Topological-DFS-Helper(i)
stack.append(v)
DFS-based-Topological-Sorting(g):
visited = new array
result = new array
stack = new array
for v in all vertex:
if visited[v] == false:
Topological-DFS-Helper(v, visited, stack)
return result.reverse()
时间复杂度:
Θ(V+E),V表示顶点的个数,E表示边的个数。 首先深度优先搜索的时间复杂度为Θ(V+E),而每次只需将完成访问的顶点存入数组中,需要O(1),所以总的复杂度也是Θ(V+E)。