广度优先与深度优先

发布于 2024-07-15 07:28:20 字数 45 浏览 9 评论 0原文

遍历树/图时,广度优先和深度优先有什么区别? 任何编码或伪代码示例都很棒。

When Traversing a Tree/Graph what is the difference between Breadth First and Depth first? Any coding or pseudocode examples would be great.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

合约呢 2024-07-22 07:28:20

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

展示差异可能是最简单的。 考虑一下树:

    A
   / \
  B   C
 /   / \
D   E   F

深度优先遍历将按此顺序访问节点。

A, B, D, C, E, F

请注意,在继续前进之前,您会一直向下一条腿。

广度优先遍历将按此顺序访问节点。

A, B, C, D, E, F

在这里,我们在向下遍历之前每个级别。

(请注意,遍历顺序存在一些模糊性,并且我在树的每个级别都维持了“读取”顺序。在任何一种情况下,我都可以在 C 之前或之后到达 B,同样我也可以到达E在F之前或之后。这个可能重要也可能不重要,取决于你的应用程序...)


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

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”行),如果您使用显式堆栈,则不会困难,但我会将其作为练习。

These two terms differentiate between two different ways of walking a tree.

It is probably easiest just to exhibit the difference. Consider the tree:

    A
   / \
  B   C
 /   / \
D   E   F

A depth first traversal would visit the nodes in this order

A, B, D, C, E, F

Notice that you go all the way down one leg before moving on.

A breadth first traversal would visit the node in this order

A, B, C, D, E, F

Here we work all the way across each level before going down.

(Note that there is some ambiguity in the traversal orders, and I've cheated to maintain the "reading" order at each level of the tree. In either case I could get to B before or after C, and likewise I could get to E before or after F. This may or may not matter, depends on you application...)


Both kinds of traversal can be achieved with the pseudocode:

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

The difference between the two traversal orders lies in the choice of Container.

  • For depth first use a stack. (The recursive implementation uses the call-stack...)
  • For breadth-first use a queue.

The recursive implementation looks like

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

The recursion ends when you reach a node that has no children, so it is guaranteed to end for
finite, acyclic graphs.


At this point, I've still cheated a little. With a little cleverness you can also work-on the nodes in this order:

D, B, E, F, C, A

which is a variation of depth-first, where I don't do the work at each node until I'm walking back up the tree. I have however visited the higher nodes on the way down to find their children.

This traversal is fairly natural in the recursive implementation (use the "Alternate time" line above instead of the first "Work" line), and not too hard if you use a explicit stack, but I'll leave it as an exercise.

雪若未夕 2024-07-22 07:28:20

理解术语:

这张图片应该能让您了解使用“宽度”和“深度”这两个词的上下文。

了解广度和深度


深度优先搜索:

深度优先搜索

  • 深度优先搜索算法的行为就好像它想要尽可能远
    尽可能快地从起点开始。

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

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

    1. 如果可能,访问相邻的未访问顶点,将其标记为已访问,然后将其压入堆栈。
    2. 如果您无法遵循规则 1,那么如果可能的话,从堆栈中弹出一个顶点。
    3. 如果您无法遵守规则 1 或规则 2,那么您就完蛋了。
  • Java代码:

    public void searchDepthFirst() { 
          // 从顶点 0 (A) 开始 
          vertexList[0].wasVisited = true; 
          显示顶点(0); 
          堆栈.推(0); 
          while (!stack.isEmpty()) { 
              int相邻顶点 = getAdjacentUnvisitedVertex(stack.peek()); 
              // 如果没有这样的顶点 
              if (相邻顶点 == -1) { 
                  堆栈.pop(); 
              } 别的 { 
                  vertexList[adjacentVertex].wasVisited = true; 
                  // 做一点事 
                  stack.push(adjacentVertex); 
              } 
          } 
          // 堆栈是空的,所以我们完成了,重置标志 
          for (int j = 0; j < nVerts; j++) 
              vertexList[j].wasVisited = false; 
      } 
      
  • 应用:深度优先搜索通常用于模拟游戏(以及现实世界中类似游戏的情况)。 在典型的游戏中,您可以选择几种可能的操作之一。 每个选择都会导致进一步的选择,每个选择都会导致进一步的选择,依此类推,形成一个不断扩大的树形可能性图。


广度优先搜索:

广度优先搜索

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

    public void searchBreadthFirst() { 
          vertexList[0].wasVisited = true; 
          显示顶点(0); 
          队列.插入(0); 
          整数v2; 
          while (!queue.isEmpty()) { 
              int v1 = 队列.remove(); 
              // 直到没有未访问过的邻居为止,获取一个 
              while ((v2 = getAdjUnvisitedVertex(v1)) != -1) { 
                  vertexList[v2].wasVisited = true; 
                  // 做一点事 
                  队列.插入(v2); 
              } 
          } 
          // 队列为空,所以我们完成了,重置标志 
          for (int j = 0; j < nVerts; j++)  
              vertexList[j].wasVisited = false; 
      } 
      
  • 应用:广度优先搜索首先查找满足以下条件的所有顶点:距起点一条边,然后是距起点两条边的所有顶点,依此类推。 如果您试图找到从起始顶点到给定顶点的最短路径,这非常有用。

希望这足以理解广度优先和深度优先搜索。 为了进一步阅读,我推荐 Robert Lafore 所著的一本优秀的数据结构书中的“图表”章节。

Understanding the terms:

This picture should give you the idea about the context in which the words breadth and depth are used.

Understanding Breadth and Depth


Depth-First Search:

Depth-First Search

  • Depth-first search algorithm acts as if it wants to get as far away
    from the starting point as quickly as possible.

  • It generally uses a Stack to remember where it should go when it reaches a dead end.

  • Rules to follow: Push first vertex A on to the Stack

    1. If possible, visit an adjacent unvisited vertex, mark it as visited, and push it on the stack.
    2. If you can’t follow Rule 1, then, if possible, pop a vertex off the stack.
    3. If you can’t follow Rule 1 or Rule 2, you’re done.
  • Java code:

    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;
    }
    
  • Applications: Depth-first searches are often used in simulations of games (and game-like situations in the real world). In a typical game you can choose one of several possible actions. Each choice leads to further choices, each of which leads to further choices, and so on into an ever-expanding tree-shaped graph of possibilities.


Breadth-First Search:

Breadth-First Search

  • The breadth-first search algorithm likes to stay as close as possible
    to the starting point.
  • This kind of search is generally implemented using a Queue.
  • Rules to follow: Make starting Vertex A the current vertex
    1. Visit the next unvisited vertex (if there is one) that’s adjacent to the current vertex, mark it, and insert it into the queue.
    2. If you can’t carry out Rule 1 because there are no more unvisited vertices, remove a vertex from the queue (if possible) and make it the current vertex.
    3. If you can’t carry out Rule 2 because the queue is empty, you’re done.
  • Java code:

    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;
    }
    
  • Applications: Breadth-first search first finds all the vertices that are one edge away from the starting point, then all the vertices that are two edges away, and so on. This is useful if you’re trying to find the shortest path from the starting vertex to a given vertex.

Hopefully that should be enough for understanding the Breadth-First and Depth-First searches. For further reading I would recommend the Graphs chapter from an excellent data structures book by Robert Lafore.

柠栀 2024-07-22 07:28:20

给定这个二叉树:

在此处输入图像描述

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

“我是 G,我的孩子是 D 和 I,我的孙子是 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

< strong>深度优先遍历:
遍历并不是一次跨越整个关卡。 相反,遍历首先深入到树的深度(从根到叶)。 但是,它比简单的向上和向下要复杂一些。

共有三种方法:

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-use-of-pre-order-and-post-order-traversal-of-binary-trees-in-computing

Given this binary tree:

enter image description here

Breadth First Traversal:
Traverse across each level from left to right.

"I'm G, my kids are D and I, my grandkids are B, E, H and K, their grandkids are 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

Depth First Traversal:
Traversal is not done ACROSS entire levels at a time. Instead, traversal dives into the DEPTH (from root to leaf) of the tree first. However, it's a bit more complex than simply up and down.

There are three methods:

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

Usage (aka, why do we care):
I really enjoyed this simple Quora explanation of the Depth First Traversal methods and how they are commonly used:
"In-Order Traversal will print values [in order for the BST (binary search tree)]"
"Pre-order traversal is used to create a copy of the [binary search tree]."
"Postorder traversal is used to delete the [binary search tree]."
https://www.quora.com/What-is-the-use-of-pre-order-and-post-order-traversal-of-binary-trees-in-computing

山田美奈子 2024-07-22 07:28:20

我认为以一种方式编写它们会很有趣,只需切换几行代码就会给你一个算法或另一个算法,这样你就会发现你的困境并不像一开始看起来那么强烈。

我个人喜欢将 BFS 解释为淹没景观:低海拔地区将首先被淹没,然后才是高海拔地区。 如果你把景观高度想象成我们在地理书上看到的等值线,很容易看出 BFS 同时填充了同一条等值线下的所有区域,就像物理学中的情况一样。 因此,将高度解释为距离或缩放成本给出了算法的非常直观的想法。

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

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

因此,对我来说,两者之间的区别在于哪种自然现象最符合其在现实生活中的传播模型(横向)。

I think it would be interesting to write both of them in a way that only by switching some lines of code would give you one algorithm or the other, so that you will see that your dillema is not so strong as it seems to be at first.

I personally like the interpretation of BFS as flooding a landscape: the low altitude areas will be flooded first, and only then the high altitude areas would follow. If you imagine the landscape altitudes as isolines as we see in geography books, its easy to see that BFS fills all area under the same isoline at the same time, just as this would be with physics. Thus, interpreting altitudes as distance or scaled cost gives a pretty intuitive idea of the algorithm.

With this in mind, you can easily adapt the idea behind breadth first search to find the minimum spanning tree easily, shortest path, and also many other minimization algorithms.

I didnt see any intuitive interpretation of DFS yet (only the standard one about the maze, but it isnt as powerful as the BFS one and flooding), so for me it seems that BFS seems to correlate better with physical phenomena as described above, while DFS correlates better with choices dillema on rational systems (ie people or computers deciding which move to make on a chess game or going out of a maze).

So, for me the difference between lies on which natural phenomenon best matches their propagation model (transversing) in real life.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文