解决方案的算法
您正在寻找一种拓扑排序算法:
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
if __name__ == '__main__':
connections = [(3, 7), (6, 5), (4, 6), (5, 3), (7, 8), (1, 2), (2, 1)]
print topological_sort(connections)
这是您的示例数据的输出:
([4, 6, 5, 3, 7, 8], [1, 2])
运行时间与边(依赖对)的数量成线性比例。
这个怎么运作
该算法是围绕一个名为 num_heads 的查找表组织的,该表保持对前辈的数量(传入箭头)进行计数。考虑具有以下连接的示例:a->h b->g c->f c->h d->i e->d f->b f->g h->d h->e i->b
,计数为:
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 曾经到达没有节点没有传入边的状态,那么这意味着存在一个无法进行拓扑排序的循环并且算法退出以显示请求的结果。