广度优先vs深度优先


Answers:


291

这两个术语区分了步行树的两种不同方式。

展示差异可能是最容易的。考虑这棵树:

    A
   / \
  B   C
 /   / \
D   E   F

一个深度优先遍历将访问节点顺序

A, B, D, C, E, F

请注意,您一直往下之前一条腿移动。

一个广度优先遍历将访问节点顺序

A, B, C, D, E, F

在这里,我们一直贯穿深入每个级别,然后再进行下去。

(请注意,遍历顺序有些歧义,我作弊要在树的每个级别上保持“读取”顺序。无论哪种情况,我都可以在C之前或之后到达B,同样,我可以到达F之前或之后的E。这可能会或可能无关紧要,取决于您的应用程序...)


两种遍历都可以通过伪代码实现:

Store the root node in Container
While (there are nodes in Container)
   N = Get the "next" node from Container
   Store all the children of N in Container
   Do some work on N

两种遍历顺序之间的差异在于对的选择Container

  • 对于深度,请先使用堆栈。(递归实现使用调用堆栈...)
  • 对于广度优先,请使用队列。

递归实现看起来像

ProcessNode(Node)
   Work on the payload Node
   Foreach child of Node
      ProcessNode(child)
   /* Alternate time to work on the payload Node (see below) */

当您到达没有子节点的节点时,递归将结束,因此对于有限的非循环图,可以保证递归结束。


在这一点上,我还是有点作弊。随着一点点的小聪明,你也可以工作在这个秩序中的节点:

D, B, E, F, C, A

这是深度优先的一种变体,在这种情况下,直到我向后走到树上之前,我不会在每个节点上进行工作。但是,我在的途中参观了较高的节点,以找到他们的孩子。

这种遍历在递归实现中是很自然的(使用上面的“ Alternate time”行而不是第一条“ Work”行),并且如果您使用显式堆栈也不会太难,但是我将其保留为练习。


@dmckee谢谢!我相信您的意思是“在Node的有效负载上工作”,对吗?
batbrat 2012年

4
可能值得注意的是,您可以修改深度优先版本以获取前缀(A, B, D, C, E, F-提出的第一个),中缀(D, B, A, E, C, F-用于排序:添加为AVL树然后读取中缀)或后缀(D, B, E, F, C, A提出的替代方法)遍历。名称由您处理根的位置给出。应该注意的是,对于二叉树,infix仅有意义。@batbrat就是这些名字...自从您问了这个时间以来,您可能已经知道了。
Theraot 2015年

@Theraot感谢您添加!是的,我确实了解这些遍历以及为什么Infix仅对二进制树有意义。
batbrat

如何确定哪种解决方案具有更好的空间或时间复杂性?
IgorGanapolsky'3

1
@IgorGanapolsky原则上应该都相同(毕竟,它们使用基本上相同的代码)。一个更有趣的问题是它们如何影响缓存和工作集,但我认为这将取决于树的形态。
dmckee ---前主持人小猫,2016年

95

了解条款:

这张图片应该使您对使用“ 广度”和“ 深度 ”一词的上下文有一个了解。

了解宽度和深度


深度优先搜索:

深度优先搜索

  • 深度优先搜索算法的作用就好像它希望尽快远离起点。

  • 它通常使用a Stack来记住到达死胡同时应该去哪里。

  • 遵循的规则:将第一个顶点A推到 Stack

    1. 如果可能,请访问相邻的未访问顶点,将其标记为已访问,然后将其压入堆栈。
    2. 如果您不能遵循规则1,则在可能的情况下,将一个顶点弹出堆栈。
    3. 如果您不能遵循规则1或规则2,那么就完成了。
  • Java代码:

    public void searchDepthFirst() {
        // Begin at vertex 0 (A)
        vertexList[0].wasVisited = true;
        displayVertex(0);
        stack.push(0);
        while (!stack.isEmpty()) {
            int adjacentVertex = getAdjacentUnvisitedVertex(stack.peek());
            // If no such vertex
            if (adjacentVertex == -1) {
                stack.pop();
            } else {
                vertexList[adjacentVertex].wasVisited = true;
                // Do something
                stack.push(adjacentVertex);
            }
        }
        // Stack is empty, so we're done, reset flags
        for (int j = 0; j < nVerts; j++)
            vertexList[j].wasVisited = false;
    }
    
  • 应用程序:深度优先搜索通常用于模拟游戏(以及现实世界中类似游戏的情况)。在典型的游戏中,您可以选择几种可能的动作之一。每个选择都会导致进一步的选择,每个选择都会导致进一步的选择,依此类推成为不断扩大的可能性树形图。


广度优先搜索:

广度优先搜索

  • 广度优先搜索算法喜欢尽可能地靠近起点。
  • 这种搜索通常使用来实现Queue
  • 遵循的规则:将起始顶点A设为当前顶点
    1. 访问与当前顶点相邻的下一个未访问的顶点(如果有),将其标记,然后将其插入队列。
    2. 如果由于没有更多未访问的顶点而无法执行规则1,请从队列中删除一个顶点(如果可能),并将其设为当前顶点。
    3. 如果由于队列为空而无法执行规则2,则操作完成。
  • Java代码:

    public void searchBreadthFirst() {
        vertexList[0].wasVisited = true;
        displayVertex(0);
        queue.insert(0);
        int v2;
        while (!queue.isEmpty()) {
            int v1 = queue.remove();
            // Until it has no unvisited neighbors, get one
            while ((v2 = getAdjUnvisitedVertex(v1)) != -1) {
                vertexList[v2].wasVisited = true;
                // Do something
                queue.insert(v2);
            }
        }
        // Queue is empty, so we're done, reset flags
        for (int j = 0; j < nVerts; j++) 
            vertexList[j].wasVisited = false;
    }
    
  • 应用程序:广度优先搜索首先查找距起点一个边缘的所有顶点,然后查找相距两个边缘的所有顶点,依此类推。如果您要查找从起始顶点到给定顶点的最短路径,这将很有用。

希望这足以理解广度优先和深度优先搜索。为了进一步阅读,我会推荐Robert Lafore撰写的出色数据结构书中的Graphs一章。


6
如果我再有十票赞成,我会这样做。
snr

@snr,您可以奖励赏金;)

@Snow,如果您告诉它的指令,我可以。我不知道该怎么办。
snr

谢谢@snr,我很高兴收到我的第一笔奖金。我很感激
Yogesh Umesh Vaity

1
谢谢@Snow,很高兴你们发现我的回答有用。
Yogesh Umesh Vaity

4

给定这个二叉树:

在此处输入图片说明

广度优先遍历:
从左到右遍历每个级别。

“我是G,我的孩子是D,而我,我的孙子是B,E,H和K,他们的孙子是A,C,F”

- Level 1: G 
- Level 2: D, I 
- Level 3: B, E, H, K 
- Level 4: A, C, F

Order Searched: G, D, I, B, E, H, K, A, C, F

深度优先遍历:一次
不能遍历整个级别。相反,遍历首先进入树的深度(从根到叶)。但是,这不仅仅是简单地上下。

有三种方法:

1) PREORDER: ROOT, LEFT, RIGHT.
You need to think of this as a recursive process:  
Grab the Root. (G)  
Then Check the Left. (It's a tree)  
Grab the Root of the Left. (D)  
Then Check the Left of D. (It's a tree)  
Grab the Root of the Left (B)  
Then Check the Left of B. (A)  
Check the Right of B. (C, and it's a leaf node. Finish B tree. Continue D tree)  
Check the Right of D. (It's a tree)  
Grab the Root. (E)  
Check the Left of E. (Nothing)  
Check the Right of E. (F, Finish D Tree. Move back to G Tree)  
Check the Right of G. (It's a tree)  
Grab the Root of I Tree. (I)  
Check the Left. (H, it's a leaf.)  
Check the Right. (K, it's a leaf. Finish G tree)  
DONE: G, D, B, A, C, E, F, I, H, K  

2) INORDER: LEFT, ROOT, RIGHT
Where the root is "in" or between the left and right child node.  
Check the Left of the G Tree. (It's a D Tree)  
Check the Left of the D Tree. (It's a B Tree)  
Check the Left of the B Tree. (A)  
Check the Root of the B Tree (B)  
Check the Right of the B Tree (C, finished B Tree!)  
Check the Right of the D Tree (It's a E Tree)  
Check the Left of the E Tree. (Nothing)  
Check the Right of the E Tree. (F, it's a leaf. Finish E Tree. Finish D Tree)...  
Onwards until...   
DONE: A, B, C, D, E, F, G, H, I, K  

3) POSTORDER: 
LEFT, RIGHT, ROOT  
DONE: A, C, B, F, E, D, H, K, I, G

用法(又名,我们为什么在乎):
我真的很喜欢深度优先遍历方法的简单Quora解释以及它们的常用方式:
“按顺序遍历将打印值(以BST(二进制搜索树)的顺序) “
”预遍历用于创建[二进制搜索树]的副本。
“后遍历用于删除[二进制搜索树]。”
https://www.quora.com/What-is-the-the-use-of-pre-order-and-post-order-traversal-binary-trees-in-computing


2

我认为以两种方式编写它们都是很有趣的:仅通过切换几行代码就可以为您提供一种算法或另一种算法,这样您便会发现您的视差并不像刚开始时那么强。

我个人喜欢将BFS解释为淹没景观:低海拔地区将首先被淹没,然后高海拔地区才会被淹没。如果您将地形海拔高度想象成等高线,就像我们在地理书籍中看到的那样,那么很容易看到BFS同时填充了同一等高线下的所有区域,就像物理学中那样。因此,将海拔高度解释为距离或按比例换算的成本可以很好地直观了解该算法。

考虑到这一点,您可以轻松地根据广度优先搜索的思想来轻松找到最小生成树,最短路径以及许多其他最小化算法。

我还没有看到对DFS的任何直观解释(仅是关于迷宫的标准解释,但它不如BFS 1和洪水泛滥那么强大),所以对我来说,BFS似乎与上述物理现象更好地关联,而DFS与理性系统上的选择困境有更好的关联(例如,人或计算机决定在国际象棋游戏中走出困境或走出迷宫)。

因此,对我而言,区别在于哪种自然现象最适合其在现实生活中的传播模型(横向)。


1
您可以使用类似的算法来实现它们,只需对DFS使用堆栈,对BFS使用队列。BFS的问题在于,您需要跟踪到目前为止看到的所有节点。DFS在物理学中。我想象替代宇宙,并且您想要一个有生命的世界,所有有根的孩子,都是不同的大爆炸,而您一直到宇宙死亡,没有生命?您将回到最后一个分支,然后尝试另一转弯,直到一切都用尽,然后进入下一个大爆炸,为新宇宙设定新的物理定律。超级直观。一个好的问题是找到一种方法将马放在棋盘中。
juanmf
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.