使用dict(python)中的特定键构建列表?

时间:2011-10-12 14:27:59

标签: python list search dictionary dijkstra

我正在用Python实现Dijkstra搜索算法。在搜索结束时,我使用前驱映射重建最短路径,从目标节点的前任开始。例如:

path = []
path.append(destination)
previous = predecessor_map[destination]
while previous != origin:
    path.append(previous)
    previous = predecessor_map[previous]

有没有办法用更少的代码行(例如列表理解)来做到这一点?

6 个答案:

答案 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_mapdict映射节点到父节点,并且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的调用推迟。 然后我们使用outputtee拆分为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缺少它