Topological Sorting知识点总结

932 阅读2分钟

拓扑排序

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)。

Sample Code, Here.