1.什么是有向无环图(简称DAG)呢?
举个浅显易懂的有向无环图拓扑排序的例子(Linux驱动匹配):
- 比如现在有abcde五个驱动模块文件,它们的具体的依赖顺序未知
- 但是给出了单个驱动模块文件会依赖其它的驱动依赖模块
1> a->bce,表示a的正常工作会依赖bce这三个驱动模块,但是bce之间的依 赖顺序又是未知的
2> b->d,b依赖d
3> c->d,c依赖d
4> e->cd,e的正常工作会依赖cd两个驱动模块,但cd之间的顺序未知
5> d->,d不依赖任何一个模块 - 现在我们要解出abcde五个驱动模块文件的争取依赖顺序,例如是a->b->c->d->e,还是a->e->c->d->b,还是都不是,我们该如何求解呢?那么有向无环图的拓扑排序就可以帮我们解决这个问题
2 .在1中的例子的正确答案我们可以用一个图来表示,即有向无环图,如下:
-
这个图比较简单我们可以一眼看出它的拓扑排序顺序为:aecbd(也就是我们1中的正确答案),但是我们要用代码算出结果来才行,遇到复杂的图一眼是看不出来的。
-
那么DAG的正确算法步骤是什么呢有两种,这里我们用的是入度表来实现的,基本步骤如下
1>找出图中0入度的顶点(入读为0也就是说圈上没有箭头指向它).
2>依次在图中删除这些顶点,删除后再找出0入度的顶点.
3>然后再删除……再找出…… .
4>直至删除所有顶点,即完成拓扑排序.
5>将删除的顶点依次从左到右放
删掉第一个入度为0的点为A之后的顺序为:a,图更新为如下:
删掉第二个入度为0的点为e之后的顺序为:a->e,图更新为如下:
删掉第三个入度为0的点为c之后的顺序为:a->e->c,图更新为如下
删掉第四个入度为0的点为b之后的顺序为:a->e->c->b,图更新为如下
最后删除掉d,最后拓扑排序的顺序为:a->e->c->b->d
3 .下面我们用python代码来实现以上的过程所得到的结果
- 将1中的依赖用字典表示为G = {‘a’: ‘bce’, ‘b’: ‘d’, ‘c’: ‘d’, ‘d’: ‘’, ‘e’: ‘cd’}
- python具体实现如下
def directed_acyclic_graph_topology_sort(graph):
in_degrees = dict((u, 0) for u in graph) # 初始化所有顶点入度为0
vertex_num = len(in_degrees)
for u in graph:
for v in graph[u]:
in_degrees[v] += 1 # 计算每个顶点的入度
Q = [u for u in in_degrees if in_degrees[u] == 0] # 筛选入度为0的顶点
Seq = []
while Q:
u = Q.pop() # 默认从最后一个删除
Seq.append(u)
for v in graph[u]:
in_degrees[v] -= 1 # 移除其所有指向
if in_degrees[v] == 0:
Q.append(v) # 再次筛选入度为0的顶点
if len(Seq) == vertex_num: # 如果循环结束后存在非0入度的顶点说明图中有环,不存在拓扑排序
return Seq
else:
print("there's a circle.")
G = {'a': 'bce', 'b': 'd', 'c': 'd', 'd': '', 'e': 'cd'}
print(directed_acyclic_graph_topology_sort(G))
['a', 'e', 'c', 'b', 'd']