以下是如何在 Python 中执行此操作:
from collections import defaultdict
def topological_sort(dependency_pairs):
'Sort values subject to dependency constraints'
num_heads = defaultdict(int) # num arrows pointing in
tails = defaultdict(list) # list of arrows going out
for h, t in dependency_pairs:
num_heads[t] += 1
tails[h].append(t)
ordered = [h for h in tails if h not in num_heads]
for h in ordered:
for t in tails[h]:
num_heads[t] -= 1
if not num_heads[t]:
ordered.append(t)
cyclic = [n for n, heads in num_heads.iteritems() if heads]
return ordered, cyclic
def is_toposorted(ordered, dependency_pairs):
'''Return True if all dependencies have been honored.
Raise KeyError for missing tasks.
'''
rank = {t: i for i, t in enumerate(ordered)}
return all(rank[h] < rank[t] for h, t in dependency_pairs)
print topological_sort('aa'.split())
ordered, cyclic = topological_sort('ah bg cf ch di ed fb fg hd he ib'.split())
print ordered, cyclic
print is_toposorted(ordered, 'ah bg cf ch di ed fb fg hd he ib'.split())
print topological_sort('ah bg cf ch di ed fb fg hd he ib ba xx'.split())
运行时间与边(依赖对)的数量成线性比例。
该算法是围绕一个名为 num_heads 的查找表组织的,该表保持对前辈的数量(传入箭头)进行计数。在ah bg cf ch di ed fb fg hd he ib
示例中,计数为:
node number of incoming edges
---- ------------------------
a 0
b 2
c 0
d 2
e 1
f 1
g 2
h 2
i 1
该算法通过“访问”没有前任的节点来工作。例如,节点a
并且c
没有传入边,因此首先访问它们。
访问意味着节点被输出并从图中移除。当一个节点被访问时,我们循环它的后继节点并将它们的传入计数减一。
例如,在访问节点a
时,我们去它的后继节点h
将其传入计数减一(这样h 2
就变成了h 1
.
同样,当访问节点时c
,我们循环遍历它的后继者f
和h
,将它们的计数减一(这样f 1
变成f 0
和h 1
变成h 0
)。
节点不再有传入边,因此我们重复输出它们并从图中删除它们的过程,直到所有节点都被访问过f
。h
在示例中,访问顺序(拓扑排序为):
a c f h e d i b g
如果 num_heads 曾经到达没有节点且没有传入边的状态,则意味着存在无法进行拓扑排序的循环并且算法退出。