我正在用Python实现Dijkstra搜索算法。在搜索结束时,我使用前驱映射重建最短路径,从目标节点的前任开始。例如:
path = []
path.append(destination)
previous = predecessor_map[destination]
while previous != origin:
path.append(previous)
previous = predecessor_map[previous]
有没有办法用更少的代码行(例如列表理解)来做到这一点?
答案 0 :(得分:7)
我唯一的建议是摆脱轻微的代码重复:
path = []
previous = destination
while previous != origin:
path.append(previous)
previous = predecessor_map[previous]
除此之外,我认为您的代码实际上非常清晰,并且不太可能从任何缩短代码的尝试中受益。
最后,值得注意的是,上述内容在destination == origin
时也有效,而原始版本可能不会(取决于填充的predecessor_map
的确切程度)。不知道这是否与您的使用案例相关。
答案 1 :(得分:4)
这可能有效:
path = [destination]
path += iter(lambda: predecessor_map[path[-1]], origin)
它的行为与原始代码相同。但是你已经写好了就好了。
如果destination
可能等于origin
:
path = []
path += iter(lambda: predecessor_map[path[-1]] if path else destination, origin)
它的行为与@aix's code相同。
答案 2 :(得分:3)
def backwalk(mymap, start, origin):
yield start
current = mymap[start]
while current != origin:
yield current
current = mymap[current]
path = list(backwalk(predecessor_map, destination, origin))
这分离了步行和收集任务。
如果您可以确保永远不会从原点开始,则可以简化为
def backwalk(mymap, start, origin):
current = start
while current != origin:
yield current
current = mymap[current]
答案 3 :(得分:1)
您可以递归遍历边缘,假设predecessor_map
是dict
映射节点到父节点,并且None
是根节点:
predecessor_map={0: None, 1: None, 2: 1, 3: 1, 4: 0, 5: 1}
定义一个反向遍历树的递归函数:
def path(node, predecessors):
return [None] if node is None else [node] + path(predecessors.get(node), predecessors)
或者,如果你敢,Y组合者:
Y=lambda f: (lambda x: f(lambda *args: x(x)(*args)))(lambda x: f(lambda *args: x(x)(*args)))
path=Y(lambda f: lambda node, p: [None] if node is None else [node] + f(p.get(node), p))
使用中(使用列表理解):
>>> print [node for node in path(None, predecessor_map)]
[None]
>>> print [node for node in path(0, predecessor_map)]
[0, None]
>>> print [node for node in path(1, predecessor_map)]
[1, None]
>>> print [node for node in path(2, predecessor_map)]
[2, 1, None]
>>> print [node for node in path(3, predecessor_map)]
[3, 1, None]
>>> print [node for node in path(4, predecessor_map)]
[4, 0, None]
>>> print [node for node in path(5, predecessor_map)]
[5, 1, None]
答案 4 :(得分:1)
另一种可能的解决方案是使用具有延迟输出的函数式编程:
from itertools import tee, chain, imap, takewhile
predecessor_map = {2:1, 3:2}
destination = 3
origin = 1
def backwalk(predecessor_map, start, origin):
def deffered_output():
for i in output:
yield i
result, a = tee(deffered_output())
b = imap(predecessor_map.get,a)
output = takewhile(lambda x: x!=origin,chain([start],b))
return result
print(list(backwalk(predecessor_map,destination,origin)))
我个人不会使用这种方法。但是,培训很有意思。
<强>解释强>
关键元素是deferred_output
,它将对output
的调用推迟。
然后我们使用output
将tee
拆分为2个迭代器。
然后我们将predecessor_map.get
应用于名为a
的第二个迭代器,并将新迭代器分配给b
。
然后我们使用takewhile
控制输出,并在到达origin
时停止。
答案 5 :(得分:0)
我不认为你可以通过理解来完成这个迭代。也许你可以简化一下,就像这样:
path, previous = [], destination
while True:
path.append(previous)
previous = predecessor_map[previous]
if previous == origin:
break
上面的循环看起来更好用do..while,但Python缺少它