拓扑排序:
几乎在所有的项目中,待完成的任务之间通常都会存在着某些依赖关系,这些关系会对它们的执行顺序形成部分约束。
对于这种依赖关系,我们通常很容易将其表示成一个有向无环路图(DAG),并将寻找其中依赖顺序的过程(寻找所有沿着特定顺序前进的边与点)成为拓扑排序(topological sorting)
解决思路:
如何归简?
首先移除其中一个节点,然后解决其余n-1个节点的问题。但,这首先要保证移除之后还是一个DAG。就是移除哪些没有入边的节点。
而清单4-9中的程序naive_topsort每次移除的都是任意的节点,这并不能保证剩余的节点还是一个DAG。清单4-10满足这样的条件,用count计数,记录每个结点的指向自己的节点个数{'a': 0, 'b': 1, 'c': 1, 'd': 2, 'e': 1, 'f': 4}
然后逐个移除count中计数为0的节点。
代码:
def naive_topsort(G, S=None):
if S is None: S = set(G) # Default: All nodes
print(S)
if len(S) == 1: return list(S) # Base case, single node
v = S.pop() # Reduction: Remove a node
seq = naive_topsort(G, S) # Recursion (assumption), n-1
min_i = 0
print("seq"," ",seq)
for i, u in enumerate(seq):
if v in G[u]: min_i = i+1 # After all dependencies
print(min_i,' ',v)
seq.insert(min_i, v)
print(seq)
return seq
def topsort(G):
count = dict((u, 0) for u in G) # The in-degree for each node
for u in G:
for v in G[u]:
count[v] += 1 # Count every in-edge
print(count)
Q = [u for u in G if count[u] == 0] # Valid initial nodes
S = [] # The result
while Q: # While we have start nodes...
u = Q.pop() # Pick one
S.append(u) # Use it as first of the rest
for v in G[u]:
count[v] -= 1 # "Uncount" its out-edges
if count[v] == 0: # New valid start nodes?
Q.append(v) # Deal with them next
return S
if __name__=="__main__":
G={
'a':set('bf'),
'b':set('cdf'),
'c':set('d'),
'd':set('ef'),
'e':set('f'),
'f':set('')
}
seq=naive_topsort(G)
print(seq)
seq2=topsort(G)
print(seq2)
运行结果:
{'d', 'e', 'c', 'f', 'a', 'b'}
{'e', 'c', 'f', 'a', 'b'}
{'c', 'f', 'a', 'b'}
{'f', 'a', 'b'}
{'a', 'b'}
{'b'}
seq ['b']
0 a
['a', 'b']
seq ['a', 'b']
2 f
['a', 'b', 'f']
seq ['a', 'b', 'f']
2 c
['a', 'b', 'c', 'f']
seq ['a', 'b', 'c', 'f']
0 e
['e', 'a', 'b', 'c', 'f']
seq ['e', 'a', 'b', 'c', 'f']
4 d
['e', 'a', 'b', 'c', 'd', 'f']
['e', 'a', 'b', 'c', 'd', 'f']
{'a': 0, 'b': 1, 'c': 1, 'd': 2, 'e': 1, 'f': 4}
['a', 'b', 'c', 'd', 'e', 'f']
由程序得出,《Python算法教程》中的清单4-9naive_topsort并不能总是得到正确的拓扑排序的结果。
清单4-10topsort是可以得到正确结果的。