拓扑排序的定义:
拓扑排序是在有向无环图(DAG)里按指向关系弹出一个序列,比如A->B,在序列里A就要在B的前面。官方定义:由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。
拓扑排序的性质:
- 每个顶点出现且只出现一次。
- 若A在序列中排在B的前面,则在图中不存在从B到A的路径。
- DAG图必然存在一个拓扑排序。
- 拓扑排序的序列不一定唯一。
拓扑排序的实现:
首先考虑一个问题,怎么找到DAG图的"起点"?第一个点就是应该没有其它结点指向它,只有它指向别人,所以它才会位于其它结点的前面。为此引入"入度"这个概念:
入度:某个结点被指的结点(边)数就是这个结点的入度。
也就是说一个DAG的起点就是入度为0 的结点,把当前起点在DAG的删除,新的入度为0的结点就是新图的起点,也就是接着弹出的结点。对于新起点来说,老起点已经弹出去了,不考虑老起点的影响,就算老起点原来指向了它也没有影响,它现在就是应该弹出的结点。
为什么一直存在新起点?:这是由DAG图的性质保证的。1.DAG图的子图是DAG图 2.DAG图中肯定有入度为0 的结点。
python代码实现:
def topsort(G):
# in_degrees保存所有结点的入度
in_degrees = dict((u, 0) for u in G)
# 计算初始入度
for u in G:
for v in G[u]:
in_degrees[v] += 1
# Q保存入度为 0 的节点
Q = [u for u in G if in_degrees[u] == 0]
# S是要弹出的序列
S = []
# 每一轮遍历所有的入度为零的结点
while Q:
u = Q.pop() # 默认从最后一个移除
'''
这块的如果从尾部弹出,下面新起点又是从尾部压入,这就是维护的一个栈,这深度优先的排序
如果是从头部弹出,这就是队列,这是宽度优先的排序
用一个对称的DAG图,就可以看出区别
'''
# 让它弹出到序列
S.append(u)
# v是入度为0指向的结点
for v in G[u]:
in_degrees[v] -= 1 # 并移除其指向,也就是入度减一
# 如果入度为0,也就是新起点,保存起来。
if in_degrees[v] == 0:
Q.append(v)
return S
G = {
'a':'bf',
'b':'cdf',
'c':'d',
'd':'ef',
'e':'f',
'f':''
}
>>> print(topsort(G))
['a', 'b', 'c', 'd', 'e', 'f']
拓扑排序的应用:
https://blog.csdn.net/qq_23262411/article/details/100034358