什么是拓扑排序
在计算机科学领域,有向图的拓扑排序是其顶点的线性排序,使得对于从顶点 u 到顶点 v 的每个有向边 uv,u 在排序中都在 v 之前。
例如,图形的顶点可以表示要执行的任务,并且边可以表示一个任务必须在另一个任务之前执行的约束;在这个应用中,拓扑排序只是一个有效的任务顺序。
如果且仅当图形没有定向循环,即如果它是有向无环图(DAG),则拓扑排序是可能的。
任何 DAG 具有至少一个拓扑排序,并且已知这些算法用于在线性时间内构建任何 DAG 的拓扑排序。
在图论中,由一个有向无环图的顶点组成的序列,当且仅当满足下列条件时,称为该图的一个拓扑排序(英语:Topological sorting):
- 每个顶点出现且只出现一次;
- 若 A 在序列中排在 B 的前面,则在图中不存在从 B 到 A 的路径。
如图所示:
直观法解 DAG
如上的无环有向图,v 表示顶点:v=['a','b','c','d','e'],e 表示有向边:e=[('a','b'),('a','d'),('b','c'),('d','c'),('d','e'),('e','c')]。
def indegree(v,e):
if len(v) == 0:
return None
v_tmp = v[:]
e_tmp = e[:]
for x in e:
if x[1] in v_tmp:#v_tmp保留没有被指向的顶点
v_tmp.remove(x[1])
if len(v_tmp) == 0:#如果v_tmp为空,则说明有回环
return -1
for i in v_tmp:
for index,x in enumerate(e_tmp):
if i in x:
e.remove(x)#将包含v_tmp中的顶点的有向边删除掉,清理完的e下次再做判断
for i in v_tmp:
v.remove(i)#清理已经处理过的顶点
return v_tmp
def topSort(v,e):
result = []
while True:
nodes = indegree(v,e)
if nodes == None:
break
if nodes == -1:
print('there\'s a circle.')
return None
result.extend(nodes)
return result
if __name__ == '__main__':
v = ['a', 'b', 'c', 'd', 'e']
e = [('a', 'b'), ('a', 'd'), ('b', 'c'), ('d', 'c'), ('d', 'e'), ('e', 'c')]
print(topSort(v,e))
归简法解 DAG
归简法求解拓扑排序,计算拓扑结构中每个节点的入度,移除入度为 0 的节点,(入度为 0 表示没有任何节点指向它),然后再判断解决剩下的节点。
def topological_sort(graph):
in_degrees = dict((u, 0) for u in graph)
for u in graph:#遍历键值
for v in graph[u]: # 根据键找出值也就是下级节点
in_degrees[v] += 1 # 对获取到的下级节点的入度加 1
# 循环结束之后的结果: {'a': 0, 'b': 1, 'c': 1, 'd': 2, 'e': 1, 'f': 4}
Q = [u for u in graph if in_degrees[u] == 0] # 找出入度为 0 的节点
in_degrees_zero = []
while Q:
u = Q.pop() # 默认从最后一个移除
in_degrees_zero.append(u) # 存储入度为 0 的节点
for v in graph[u]:
in_degrees[v] -= 1 # 删除入度为 0 的节点,以及移除其指向
if in_degrees[v] == 0:
Q.append(v)
return in_degrees_zero
if __name__ == '__main__':
# 用字典的键值表示图的节点之间的关系,键为所有顶点。值是该顶点指向的顶点。
graph_dict = {
'a': 'bf', # 表示 a 指向 b 和 f
'b': 'cdf',
'c': 'd',
'd': 'ef',
'e': 'f',
'f': ''
}
t = topological_sort(graph_dict)
print(t)
本文作者为hresh,转载请注明。