最短路径:DFS、BFS 还是两者兼而有之?

Posted

技术标签:

【中文标题】最短路径:DFS、BFS 还是两者兼而有之?【英文标题】:Shortest path: DFS, BFS or both? 【发布时间】:2013-01-24 22:30:48 【问题描述】:

我知道仅 BFS 就可以在未加权的图中找到最短路径,但我还在几个网站上看到人们声称 BFS 或 DFS 都可以做到这一点。我只是想确认这些可能是错误的,并且只有 BFS 可以做到这一点(即使在进行了快速的谷歌搜索之后,我也不是完全有信心)。如果我不正确,有人可以解释一下 DFS 如何给出最短路径。

【问题讨论】:

这也不属于这里,它与它所属的站点上的条目重复 cs.stackexchange.com/questions/4914/… 【参考方案1】:

DFS 不一定会在无向图中产生最短路径。 BFS 将是这里的正确选择。

例如,考虑一个通过取三角形的角并将它们连接起来形成的图形。如果您尝试使用 DFS 找到从一个节点到另一个节点的最短路径,那么您将得到错误的答案,除非您沿着直接连接起始节点和目标节点的边。

正如@nhahtdh 在下面指出的那样,有一种称为迭代深化的 DFS 变体,在该变体中,您运行 DFS 的最大深度为 1,然后是 2,然后是 3,依此类推,直到找到目标。这将始终找到最短路径,并且比 BFS 使用更少的内存。

希望这会有所帮助!

【讨论】:

【参考方案2】:

迭代加深搜索(IDS),它由多轮深度限制搜索(基本上是DFS,但如果达到深度限制d就停止搜索)从1逐渐增加深度,将找到最短路径未加权的图。

// Iterative deepening search
// isGoal is a function that checks whether the current node is the goal
function ids(graph, start, isGoal)  
    maxDepth = 1
    do 
        found = dls(graph, start, isGoal, maxDepth, 0)
        // Clear the status whether a node has been visited
        graph.clearVisitedMarker();
        // Increase maximum depth
        depth = depth + 1

    // You can slightly modify the dls() function to return whether there is a
    // node beyond max depth, in case the graph doesn't have goal node.
     while (found == null)

    return found


// Depth-limited search
// isGoal is a function that checks whether the current node is the goal
function dls(graph, currentNode, isGoal, maxDepth, currentDepth) 
    if (graph.visited(currentNode)) 
        return null
    
    graph.setVisited(currentNode)

    if (isGoal(currentNode)) 
        return currentNode
    

    // Stop searching when exceed max depth
    // This is the only difference from DFS
    if (currentDepth >= maxDepth) 
        return null
    

    for (adjNode in graph.getAdjacent(currentNode)) 
        found = dls(graph, adjNode, goal, maxDepth, currentDepth + 1)
        if (found != null) 
            return found
        
    

    return null

它起作用了,因为您逐渐增加了与起始节点允许的距离:由于深度限制,距离为 a + 1 的节点不会在距离为 a 的节点之前探索。

一个常见的问题是距离为 a 的节点将被重新访问 (d - a + 1) 次,其中 d 是到达目标的最短路径的深度。如果我们要谈论性能,这取决于图或搜索树。在具有大分支因子的搜索树上,在深度 d 处生成的节点成为主导项,因此重访问题不大。由于指数空间要求,BFS 无法用于这种情况,但 IDS 将仅使用多项式空间。

【讨论】:

对于大图来说,这个算法确实比 BFS 快,但是我相信你需要为递归 dls 调用保留一组单独的访问节点。否则,根据 for 循环在 graph.getAdjacent(currentNode) 上的迭代方式,您可能会阻塞正确的路径并获得不正确的结果。我在 python 中实现了这个并使用了一个集合来存储相邻的节点(这是无序的)并且得到了一个非常难以诊断的错误,有时它会给我正确的答案,有时它不会。【参考方案3】:

要理解的观点:没有权重和方向的图中的 BFS 与 Dijkstra(权重=1,一个方向)相同,因此了解 Dijkstra 为何正确可能会有所帮助。如果我通过了,将添加更多详细信息。

【讨论】:

【参考方案4】:

我知道这里聚会晚了,但是。 DFS 和 BFS 之间有几个区别(简短回答:它们都可以在未加权的图中找到最短路径)。

    BFS:通常由队列实现(先进先出数据结构) 当您逐层耗尽所有邻居顶点直到到达目标节点并在该节点处停止时,例如:如果未找到所有该节点层进入队列,则从第 1 层到达所有节点,并继续为第 2 层和很快。它将保证目标节点是目前找到的最短层(因为它在找到目标节点后停止,它不会遍历图中的所有节点(它在速度方面比DFS快))

    DFS:通常由堆栈实现(先进后出数据结构) DSF 从给定点耗尽所有节点,直到它无法再探索。但是当它找到目标节点时,不能保证路径是最短路径,所以它必须遍历图中的所有节点以确保路径是最短的。 顺便说一句

@alandong 回答错误

【讨论】:

【参考方案5】:

如果你实施得当,BFS 和 DFS 都会给出从 A 到 B 的最短路径。

让我们把整个图想象成一棵树。基本上,BFS 将运行每一层树并通过遍历所有层来找出路径。相比之下,DFS 将运行到每个叶节点,并在沿着该路径遍历节点时找出路径。它们都使用了穷举寻路搜索,因此,如果您正确实施这些算法,它们都将保证找到最短路径。

使用 BFS 和 DFS 的优缺点如下:

BFS,使用更多内存,遍历所有节点

DFS,使用更少的内存,如果你幸运地选择了包含你感兴趣的节点的叶子节点路径,可能会稍微快一些。(不一定要遍历所有节点)。

【讨论】:

但是你必须确保通往这片叶子的路径是最短的,不是吗? 你只是在谈论树木,对吧?因为你的推理不适合图表【参考方案6】:

我对使用 dfs 在未加权图上寻找最短路径以及在加权图上寻找最小加权路径的理解:

A) Dfs 也可以求解最短路径(也就是最小加权路径)。唯一的缺点是多条边重新访问已经访问过的节点会导致指数时间复杂度。

B) 如果要求是找到最短路径的长度(也就是最小的权重):缓存从源到当前节点的距离似乎首先可以解决上述时间复杂度问题。但事实上不,它仍然没有。因为每次为缓存节点提供更好的距离(即,从源到当前节点的新路径的距离小于当前节点已缓存的距离),必须重新计算该节点。

C) 结论:Dfs 解决了最短路径(也是最小权重)问题,但它从来都不是最优的。相反,它在时间上最差(读取指数)。

【讨论】:

以上是关于最短路径:DFS、BFS 还是两者兼而有之?的主要内容,如果未能解决你的问题,请参考以下文章

动画演示广度优先算法寻找最短路径

LeetCode1293网格中的最短路径(DFS和BFS)分析

模板 图的遍历 bfs+dfs 图的最短路径 Floyed+Dijkstra

经典图算法Java代码实践:BFS,DFS以及几种最短路径算法

2/10 并查集+bfs+dfs+最短路径+spfa队列优化

最短路径问题