代码中的每个递归调用都会有一次迭代,其中 j == end 为真,因此您确实会多次添加它:与递归深度一样多。
如果将结束条件放在循环之外会更好,如下所示:
if start == end:
return [start]
因此,只需稍加修改,您的代码将如下所示:
def get_path(sol, start, end):
if start == end:
return [start]
out = []
for i,j in sol:
if i == start:
out.append(i)
out += get_path(sol, j, end)
return out
其他几点说明:
-
您可以一次性执行if块中的两个列表操作:
out += [i] + get_path(sol, j, end)
-
遍历每个中的完整列表是非常低效的
递归调用。最好先建一个字典,keyed
由起始编号。这样你就不必反复寻找
对应的边。
-
您的代码似乎希望该图是一个链,没有任何
您有 2 个或更多传出边的节点。如果发生这种情况,if i == start: 块将执行多次,从而导致输出尴尬。这种情况也不太难涵盖
代码:
from collections import defaultdict
def get_path(sol, start, end):
# Transform to dict
d = defaultdict(list)
for i,j in sol:
d[i] += [j]
def recur(start):
if start == end:
return [start]
for nxt in d[start]:
path = recur(nxt)
if path is not None:
return [start] + path
return recur(start)
edges = [(0, 10),(2, 16),(4, 5),(6, 24),(7, 6),(8, 23),(9, 25),(10, 14),
(11, 1),(12, 19),(13, 22),(14, 15),(15, 11),(16, 7),(17, 21),
(18, 13),(19, 17),(20, 8),(21, 3),(22, 20),(23, 12),(24, 9),(25, 18)]
path = get_path(edges, 2, 3)
print (path)
循环图
你的问题是关于无环图,因为你写的:
只有一条路
但是对于任何来这里寻找循环图解决方案的人来说,上述解决方案需要扩展,以免陷入循环。这可以通过将节点标记为已访问来完成:
from collections import defaultdict
def get_path(sol, start, end):
# Transform to dict
d = defaultdict(list)
for i,j in sol:
d[i] += [j]
visited = set()
def recur(start):
if start == end:
return [start]
visited.add(start)
for nxt in d[start]:
if nxt not in visited:
path = recur(nxt)
if path is not None:
return [start] + path
visited.remove(start)
return recur(start)
edges = [(0,1),(1,0),(0,2),(2,0),(2,3),(3,2)]
path = get_path(edges, 2, 3)
print (path)
请注意,这两种解决方案都不能保证找到的路径是最短的。但是如果知道只有一条路径,那就是一种内存效率高的算法。
如果有多个路径,则深度优先搜索在找到路径时无法停止搜索,因为可能仍会找到更短的路径。另一方面,如果您使用广度优先搜索,那么您可以在找到路径后立即停止。