BFS输出图的最短路径

时间:2013-12-03 11:14:41

标签: python algorithm graph-algorithm shortest-path breadth-first-search

我正在尝试创建一个回溯函数,它将返回从根到GOAL

的最短路径顺序列表

我的path_holder:

path_holder = {
   'node1':['node2','node3','node5'],  
   'node2':['node1','node8','node10'],
   'node3':['node4','node6']},
   'node4':['node2','node1','node3'],
   'node5':['DEADEND'],
   'node6':['GOAL']
    ....
    }

在我的path_holder输入中,它是BFS的输出,因此第一个节点是根,最后一个节点是目标。因为path_holder输入是BFS的输出,所以当找到GOAL时它会停止,因此所有搜索GOAL所需的先前节点的分支的节点都会添加到{ {1}}也是。

目前我陷入了发生无限循环的while循环。我的一般策略是从path_holder节点开始,并使用此节点的密钥来查找此密钥在另一个密钥(节点)列表中的位置。一旦我找到该节点(其列表包含密钥),我将节点的密钥设置为新目标。 (令人困惑的句子抱歉)

这个图可能包含循环,这可能就是为什么我也会得到无限循环。

我的回溯功能:

GOAL

ex:输出

def backtrace(path_holder, root, goal):
    dct = {}
    for d in path_holder:
            dct.update(d)
    rootnode = root.keys()[0]
    goal = goal.keys()[0]
    #x = len(path_holder)
    path = []
    path.append(goal)
    #for i in reversed(xrange(x):
    #   path_holder[i].keys()
    while goal != rootnode: 
        # find key that contains goal in list
        for i in dct:
            #print i
            for j in dct[i] :
                if j not in path:
                    if j == goal:
                        path.append(i)
                        goal = i
        # append key that has goal in the list
        # set goal to be the key that was appended
        # repeat
    return path

1 个答案:

答案 0 :(得分:6)

1。虫子

  1. 运行代码时出现以下错误:

    >>> backtrace(path_holder, 'node1', 'GOAL')
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "q20349609.py", line 13, in backtrace
        dct.update(d)
    ValueError: dictionary update sequence element #0 has length 1; 2 is required
    

    那是因为当你迭代这样的字典时:

    for d in path_holder:
    

    你得到的是字典的。因此d取值为'node1''node2'等等,您无法将这些值传递给dict.update方法。

    但是你到底想要做什么呢?如果您尝试将path_holder复制到dct,则可以写下:

    dct = dict(path_holder)
    

    但为什么还要复制呢?为什么不使用path_holder

  2. 修复了错误#1,程序运行但陷入无限循环。那是因为这些方面:

    if j not in path:
        if j == goal:
            path.append(i)
    

    这些行意味着只有路径中的节点({1}}尚未在路径中,但等于目标时,才会向路径添加节点。但是等一下,j已经在这一点上了。因此,两个条件不能同时满足。因此,没有任何东西被添加到路径中!

    显然行:

    goal

    应该是:

    if j not in path:
    

    因为if i not in path: 是我们正在考虑添加到路径的节点。

  3. 修复了错误#1和#2,程序取得了一些进展,但仍陷入无限循环。如果我们在i之后添加行print(path),那么我们会得到以下输出,直到它被卡住:

    path.append(i)

    您可以看到搜索出错了:从>>> backtrace(path_holder, 'node1', 'GOAL') ['GOAL', 'node6'] ['GOAL', 'node6', 'node3'] ['GOAL', 'node6', 'node3', 'node4'] 已经转到node3,但没有从node4node4的路线,除了经历GOAL的那个。搜索永远不会考虑在路径中添加node3,因为它已经存在。

  4. 2。该怎么做

    当您找到node3之类的节点的路径时,您无法知道该节点是否位于从node4GOAL的最短路径上。您现在所能知道的是如果 node1位于从node4GOAL的最短路径上,那么你'我会通过node1到达那里。这就是你必须记录的一切。

    以下是我实现此操作的方法,使用字典node3为每个节点记录从visited到该节点的最短路径上的上一个节点,以及collections.deque来维护我们可能尚未访问过的邻居节点队列。

    start

    注意:

    1. 您的变量from collections import deque class NotFound(Exception): pass def search(graph, start, goal): """Find the shortest path from start to goal in graph (which must be a map from a node to an iterable of adjacent nodes), using breadth-first search. >>> graph = { ... 1: [2, 4, 5], ... 2: [1], ... 3: [4, 6], ... 4: [2, 1, 3], ... 5: [], ... 6: [7], ... 7: [], ... } >>> search(graph, 1, 7) [1, 4, 3, 6, 7] >>> search(graph, 1, 1) [1] >>> search(graph, 5, 1) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... NotFound: No path from 5 to 1 """ visited = {start: None} queue = deque([start]) while queue: node = queue.popleft() if node == goal: path = [] while node is not None: path.append(node) node = visited[node] return path[::-1] for neighbour in graph[node]: if neighbour not in visited: visited[neighbour] = node queue.append(neighbour) raise NotFound('No path from {} to {}'.format(start, goal)) 包含graph中称为adjacency list representation的数据结构。所以我调用了这个变量path_holder

    2. 我写了一个docstring来解释函数的作用以及如何调用它。 docstring还包含可以使用doctest模块运行的嵌入式代码示例。

    3. 您的功能会从目标向后搜索到开始。但这与从开始到目标的向前搜索一样,所有边缘都被颠倒了。所以我通过搜索来保持简单。