在深度优先搜索中跟踪和返回路径

Posted

技术标签:

【中文标题】在深度优先搜索中跟踪和返回路径【英文标题】:Tracing and Returning a Path in Depth First Search 【发布时间】:2012-10-03 13:27:08 【问题描述】:

所以我有一个问题,我想使用深度优先搜索来解决,返回 DFS 找到的第一个路径。这是我的(不完整的)DFS 函数:

    start = problem.getStartState()
    stack = Stack()
    visited = []
    stack.push(start)
    if problem.isGoalState(problem.getStartState):
        return something
    while stack:
        parent = stack.pop()
        if parent in visited: continue
        if problem.isGoalState(parent):
            return something
        visited.append(parent)
        children = problem.getSuccessors(parent)
        for child in children:
            stack.push(child[0])

startState 和goalState 变量只是x、y 坐标的元组。问题是一个有多种方法的类。这里重要的是 getSuccessors(它以 3 项元组列表的形式返回给定状态的子项。但是对于这部分问题,只有元组的第一个元素 (child[0]),它返回子节点在 x、y 坐标中的状态,很重要)和 isGoalState(它提供目标状态的 x、y 坐标)。

所以我认为(在这一点上很难测试),这个函数,如果其他一切都正确实现,一旦达到目标状态,它就会返回。如果我遗漏了什么,请告诉我。不过,我最大的问题是返回什么。我希望它按从头到尾的顺序输出到达目标状态所需的所有状态的列表。似乎简单地返回我的堆栈并不能解决问题,因为堆栈将包含许多未访问的孩子。我的访问列表也不会产生任何有用的信息,因为可以想象我可能会到达死胡同,不得不回溯,但访问列表中仍然有死胡同。我将如何获得我想要的列表?

【问题讨论】:

祝你的 Pacman AI 作业好运 ;) ai.berkeley.edu/project_overview.html 【参考方案1】:

你是对的 - 你不能简单地返回堆栈,它确实包含很多未访问的节点。

但是,通过维护一个地图(字典):map:Vertex->Vertex 这样parentMap[v] = the vertex we used to discover v,您可以获得您的路径。

您需要做的修改几乎在 for 循环中:

    for child in children:
        stack.push(child[0])
        parentMap[child] = parent #this line was added

稍后,当你找到你的目标时,你可以得到从源到目标的路径(伪代码):

curr = target
while (curr != None):
  print curr
  curr = parentMap[curr]

注意顺序会颠倒,可以通过将所有元素压入堆栈然后打印来解决。

我曾经在this thread 中回答了关于在 BFS 中查找实际路径的类似(尽管不是完全相同的 IMO)问题

另一种解决方案是使用递归版本的 DFS 而不是迭代 + 堆栈,一旦找到目标,将递归中的所有 current 节点打印备份 - 但此解决方案需要将算法重新设计为递归一个。


附:请注意,如果图形包含无限分支,DFS 可能无法找到到目标的路径(即使维护visited 集)。 如果您想要一个完整的(如果存在,总是找到解决方案)和最佳(找到最短路径)算法 - 如果您有一些启发式函数,您可能想要使用 BFS 或 Iterative Deepening DFS 甚至 A* Algorithm /p>

【讨论】:

不错的解决方案。 . .为了获得正确的订单,这样的事情也可以吗?: 治疗时!= 无:newList.append(curr) curr = parentMap[curr]。然后打印(newList.reverse())?抱歉缺少换行符,无法在 cmets 中进行。 @user1427661: (1) 您有时使用cure,有时使用curr - 它应该是其中之一。 (2) 是的,它应该可以工作 (3) 阅读我关于选择 DFS 作为算法的编辑 - 你应该知道它的缺点。 @amit 如果节点具有双面边(即 Edge(u,v) 和 Edge(v,u) 存在),这实际上不起作用。 for child in children 而不是 parentMap[child] = parent 你需要检查孩子是否已经被添加到 parentMap (即if(!parentMap.containsKey(child))parentMap.put(child, parent);【参考方案2】:

不是针对您的问题,但是您可以调整此代码并将其应用于不同的场景,实际上,您可以使堆栈也保持路径。

例子:

     A
   /    \
  C      B
  \     / \
   \    D E
    \    /
       F
       
graph = 'A': set(['B', 'C']),
         'B': set(['A', 'D', 'E']),
         'C': set(['A', 'F']),
         'D': set(['B']),
         'E': set(['B', 'F']),
         'F': set(['C', 'E'])

def dfs_paths(graph, start, goal):
    stack = [(start, [start])]
    visited = set()
    while stack:
        (vertex, path) = stack.pop()
        if vertex not in visited:
            if vertex == goal:
                return path
            visited.add(vertex)
            for neighbor in graph[vertex]:
                stack.append((neighbor, path + [neighbor]))

print (dfs_paths(graph, 'A', 'F'))   #['A', 'B', 'E', 'F']

【讨论】:

不错的代码,是否有可能获得所有可能的路径? 很好的解决方案!【参考方案3】:

此链接应该对您有很大帮助...这是一篇冗长的文章,广泛讨论了返回路径的 DFS 搜索...我觉得它比我或其他任何人可以发布的任何答案都要好

http://www.python.org/doc/essays/graphs/

【讨论】:

好吧,它更一般地谈论图表......但如果你检查它确实是 DFS......而且它是鸭式的,因此他们可以复制/粘贴和查看结果,不像 OP......跨度> (删除了之前过于苛刻的评论,这是改写) OP 询问 DFS 的 特定 问题 - 用通用链接回答似乎不是我的答案.至少将他引导到文章中回答您认为的问题的相关部分。 整件事都是关于返回路径......这是问题所在,......说你针对他的具体问题量身定制的答案肯定回答了 OP......但这篇文章可能仍然是很好的阅读给他的素材……提供了一个非常不错的图探索功能,简洁明了……【参考方案4】:

我刚刚在php 中实现了类似的东西。

背后的基本思想如下:为什么要维护另一个堆栈,当有调用堆栈时,它在执行的每个点都反映了从入口点获取的路径。当算法达到目标时,您只需要返回当前调用堆栈,这会导致读取向后的路径。这是修改后的算法。请注意return immediately 部分。

/**
 * Depth-first path
 * 
 * @param Node $node        Currently evaluated node of the graph
 * @param Node $goal        The node we want to find
 *
 * @return The path as an array of Nodes, or false if there was no mach.
 */
function depthFirstPath (Node $node, Node $goal)

    // mark node as visited
    $node->visited = true;

    // If the goal is found, return immediately
    if ($node == $goal) 
        return array($node);
    

    foreach ($node->outgoing as $edge) 

        // We inspect the neighbours which are not yet visited
        if (!$edge->outgoing->visited) 

            $result = $this->depthFirstPath($edge->outgoing, $goal);

            // If the goal is found, return immediately
            if ($result) 
                // Insert the current node to the beginning of the result set
                array_unshift($result, $node);
                return $result;
            
        
    

    return false;

【讨论】:

以上是关于在深度优先搜索中跟踪和返回路径的主要内容,如果未能解决你的问题,请参考以下文章

如何在广度优先搜索中跟踪深度?

深度优先搜索和广度优先搜索、A星算法三种算法的区别和联系?

如何跟踪广度优先搜索的深度?

深度优先搜索DFS

深度优先搜索---一个懵逼的东东

基本算法——深度优先搜索(DFS)和广度优先搜索(BFS)