【问题标题】:Difference between DFS vs BFS in this example?本例中 DFS 与 BFS 的区别?
【发布时间】:2018-11-16 10:25:20
【问题描述】:

在阅读了DFS 打印输出的示例后,我有点困惑 两种 DFS 方法都以不同的方式进行,尽管两者都被称为 DFS。

事实上,DFS 递归方法打印输出的方式与 BFS。那么有什么区别呢?这里给出的递归方法不是 DFS 的例子吗?

使用堆栈

// Iterative DFS using stack
    public  void dfsUsingStack(Node node)
    {
        Stack<Node> stack=new  Stack<Node>();
        stack.add(node);
        node.visited=true;
        while (!stack.isEmpty())
        {
            Node element=stack.pop();
            System.out.print(element.data + " ");

            List<Node> neighbours=element.getNeighbours();
            for (int i = 0; i < neighbours.size(); i++) {
                Node n=neighbours.get(i);
                if(n!=null && !n.visited)
                {
                    stack.add(n);
                    n.visited=true;

                }
            }
        }
    }

输出是 40,20,50,70,60,30,10

递归方法

// Recursive DFS
    public  void dfs(Node node)
    {
        System.out.print(node.data + " ");
        List neighbours=node.getNeighbours();
        node.visited=true;
        for (int i = 0; i < neighbours.size(); i++) {
            Node n=neighbours.get(i);
            if(n!=null && !n.visited)
            {
                dfs(n);
            }
        }
    }

输出是 40,10,20,30,60,50,70 与BFS下的输出相同

那么有什么区别呢?

【问题讨论】:

  • 您可能希望将您正在遍历的图表作为该问题的一部分。否则,访问过的节点列表毫无意义。
  • 在某些图中,DFS 和 BFS 会产生相同的遍历顺序。没有错。你在遍历什么图?
  • 补充@AnT 的答案:由于邻居/边添加到图中的顺序,BFS 和反向 DFS(或伪 DFS,如 AnT 所称)也只是巧合它)同时遍历相同的节点。如果你的 BFS 实现中邻居的访问顺序颠倒了,你会得到不同的结果。
  • 你在编造一些东西。您的图片不正确,或者您的第二个输出不正确,或者其他内容不正确。如果第二个实现决定以40, 10, ... 开始,那么它总是必须转到30 下一个:40, 10, 30...。然而,您声称您的第二个实现以某种方式产生了40,10,20,30,60,50,70。这是不可能的。我怀疑边缘10-30 并不真正存在于您的实际数据中。
  • @AnT 我已经给出了图片来源和输出基于相同程序的链接

标签: algorithm graph-theory depth-first-search breadth-first-search


【解决方案1】:

虽然最终结果(路径)可能相同,但 bfs 和 dfs 之间的根本区别(不是发布的具体实现)在于搜索机制。
一个明显的例子是只存在一条路径的情况。在这种情况下,任何好的搜索算法(无论是 dfs、bfs 还是其他)最终都会找到一条路径。
在存在多个路径的情况下,bfs 和 dfs 可能会找到相同的路径或不同的路径。 bfs 的特点之一是它找到了最短路径。
搜索机制的核心区别在于 bfs 在所有方向上均等地探索(因此称为广度),而 dfs 一直探索一个(通常是随机)方向(因此称为深度),如果没有找到解决方案则“回溯”。
有许多资源可以显示 bfs 和 dfs 的可视化表示,例如 this一。
这是我创建的用于演示和测试遍历算法的工具的屏幕截图:

通过查看代表已探索节点的灰点,您可以看到 bfs 的性质,类似于洪水。

【讨论】:

  • 非常好的例子
【解决方案2】:

第一种方法根本不是 DFS,至少在 DFS 算法的规范定义中。这只是 BFS,其中有人用堆栈替换了队列。但是,这种实现足以“模仿” DFS,因为它以类似 DFS 的顺序发现图顶点。如果您愿意,它可以称为“伪 DFS”,因为类似于 DFS 的发现顺序,但规范的 DFS 算法远不止于此。

规范的 DFS 不仅遵循深度优先的顶点发现顺序,而且还产生一定的“未发现”顺序,一定的回溯顺序(即经典 Dijkstra 命名法中"gray" vertex becomes "black" vertex 的时刻)。在第一个实现中,规范 DFS 的这一重要特性不存在(或者不可能以任何合理的方式实现)。

同时,第二种方法是经典 DFS 算法的显式递归实现。

在任何情况下,无论是真正的 DFS 还是伪 DFS,都没有应该访问顶点的“一个真正的顺序”。两种实现都可以产生相同的顶点访问顺序。在您的情况下,没有人只是费心确保这一点。输出的差异是由简单的事实造成的,即前一个实现以 reverse 顺序访问邻居 - 从最后到第一个。所有邻居首先被压入堆栈,然后一个接一个地弹出以供访问。堆栈的 LIFO 行为是产生反转的原因。同时,后一个实现按 forward 顺序访问邻居 - 从第一个到最后一个。

如果您将后一个实现中的循环替换为

for (int i = neighbours.size() - 1; i >= 0; --i) 

您应该在两个实现中得到相同的访问顺序(相同的输出)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-08
    • 1970-01-01
    • 1970-01-01
    • 2017-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多