当前位置:   article > 正文

图的遍历(深度优先和广度优先)_优先和广度优先遍历等方法

优先和广度优先遍历等方法

        图的遍历是指从某个顶点开始,沿着特定的搜索路径访问图中的每一个顶点各一次的过程。图的遍历算法是解决图连通性问题、拓扑排序和寻找关键路径等问题的基础。

 


前言

        相比于树结构的遍历,图的遍历要复杂得多,因为图中的任何一个节点都可能与其它所有节点相邻接。这就意味着,在访问了某个节点之后,可能会顺着某条路径再次返回到这个节点。为了防止对节点进行重复访问,在图的遍历过程中需要记录下已经访问过的节点。深度优先搜索(DFS)和广度优先搜索(BFS)是两种基本的图遍历方法。


图的遍历图解

深度优先

904804bf444a4473a94848df3a986dbc.png

广度优先

e961ba158fbd479e8cbbe4e493c6eb2f.png

一、深度优先遍历是什么?

        深度优先遍历(Depth-First Search, DFS)是一种用于遍历或搜索树或图结构的算法。深度优先遍历的基本思想是从根节点(或任意一个节点)开始,沿着一条路径尽可能深地搜索,直到无法继续为止,然后回溯到上一个分叉点,再尝试其他可能的路径。这个过程一直进行,直到所有的节点都被访问过。举一个简单的列子,就是一条路走到黑,不撞南墙你不回头。

二、深度优先遍历的原理

        我们可以将其比作一场寻宝游戏。想象一下,你站在一片未知的土地上,目标是找到隐藏的宝藏。你会选择一条路径前进,直到遇到岔路口。此时,你会如何选择?深度优先遍历是让你选择其中一条路走到尽头,如果发现没有宝藏,就返回到刚刚遇到的岔路口,尝试其他的路径。这个过程会一直重复,直到所有的路径都被探索过。

三、深度优先遍历的实现

1.递归实现

        在递归实现中,算法首先访问当前节点,然后递归地访问其子节点。如果一个节点没有子节点或所有子节点都已被访问,算法将回溯到该节点的父节点,并尝试访问其他分支,关键代码(C语言)如下:

  1. void dfs(int node) {
  2. visited[node] = 1;
  3. printf("访问节点 %d", node);
  4. for (int i = 0; i < num_nodes; i++) {
  5. if (graph[node][i] == 1 && !visited[i]) {
  6. dfs(i);
  7. }
  8. }
  9. }

下面是对代码的详细解释:
1.void dfs(int node)定义了一个名为 `dfs` 的函数,它接受一个整数参数 `node`,表示当前访问的节点。
2. visited[node] = 1;将当前节点标记为已访问。这是为了避免重复访问同一个节点。
3. printf("访问节点 %d", node);打印当前访问的节点编号。
4. for (int i = 0; i < num_nodes; i++) { ... }使用 for 循环遍历所有节点。`num_nodes` 表示图中节点的数量。
5. if (graph[node][i] == 1 && !visited[i]) { ... } 判断当前节点 `node` 与节点 `i` 之间是否存在边,并且节点 `i` 是否未被访问过。如果满足条件,则执行下一步。
6.dfs(i);递归调用 `dfs` 函数,以节点 `i` 作为新的当前节点进行深度优先遍历。
在每次递归调用中,都会访问一个新的节点,并将其标记为已访问。然后,继续递归地访问与该节点相邻的未访问过的节点,直到所有可达的节点都被访问过为止。

2.栈实现

        我们可以使用栈来存储待访问的节点。每次从栈中弹出一个节点进行处理,并将其未访问的邻居节点压入栈中,直到栈为空,关键代码(Java)如下:

  1. import java.util.Stack;
  2. public class DFS {
  3. private Stack<Integer> stack;
  4. public DFS() {
  5. stack = new Stack<>();
  6. }
  7. public void dfs(int[][] graph, int start) {
  8. boolean[] visited = new boolean[graph.length];
  9. stack.push(start);
  10. while (!stack.isEmpty()) {
  11. int node = stack.pop();
  12. if (!visited[node]) {
  13. visited[node] = true;
  14. System.out.println("访问节点:" + node);
  15. for (int i = 0; i < graph[node].length; i++) {
  16. if (graph[node][i] == 1 && !visited[i]) {
  17. stack.push(i);
  18. }
  19. }
  20. }
  21. }
  22. }
  23. }

下面是对代码的详细解释:
1. 定义一个栈`stack`,用于存储待访问的节点。
2. 定义一个构造函数`DFS()`,初始化栈。
3. 定义一个`dfs()`方法,接收一个邻接矩阵`graph`表示图的结构,以及一个起始节点`start`。
4. 在`dfs()`方法中,首先定义一个布尔数组`visited`,用于记录每个节点是否被访问过,初始值都为`false`。
5. 将起始节点`start`压入栈中。
6. 进入循环,当栈不为空时,执行以下操作:
   a. 弹出栈顶元素,即当前要访问的节点`node`。
   b. 如果该节点`node`未被访问过,则将其标记为已访问,并输出访问信息。
   c. 遍历与该节点相邻的所有节点,如果相邻节点未被访问过且与当前节点有边相连(即`graph[node][i] == 1`),则将相邻节点压入栈中。
7. 循环结束后,所有可达节点都已访问过,遍历结束。

四、广度优先遍历是什么?

        广度优先遍历(Breadth-First Search, BFS)是一种用于遍历或搜索树或图结构的算法。与深度优先遍历不同的是,广度优先遍历是从某个顶点出发,逐层向外探索,直到所有相通的顶点都被访问为止。

五、广度优先遍历的原理

        广度优先遍历通常使用队列这种数据结构来实现。队列遵循“先进先出”的规则,这与广度优先遍历的“逐层推进”规则相契合,BFS使用队列这种数据结构来存储待访问的节点。在遍历过程中,首先访问起始节点,然后将其邻接节点加入队列。之后,从队列中取出节点并访问其邻接节点,重复这一过程直到队列为空。

六、广度优先遍历的实现

  1. public static void bfs(Map<Character, List<Character>> graph, char start) {
  2. Set<Character> visited = new HashSet<>(); // 用于记录已访问的节点
  3. Queue<Character> queue = new LinkedList<>(); // 使用队列存储待访问的节点
  4. queue.add(start);
  5. visited.add(start);
  6. while (!queue.isEmpty()) {
  7. char vertex = queue.poll(); // 取出队列中的第一个节点
  8. System.out.print(vertex + " "); // 访问该节点
  9. // 将邻接节点加入队列
  10. for (char neighbor : graph.get(vertex)) {
  11. if (!visited.contains(neighbor)) {
  12. queue.add(neighbor);
  13. visited.add(neighbor);
  14. }
  15. }
  16. }
  17. }

        我们使用了一个队列来存储待访问的节点,并使用一个集合来记录已访问过的节点。当队列不为空时,我们从队列中取出一个节点并访问它,然后将它的邻接节点加入队列。这个过程一直重复,直到队列为空。


总结

        DFS更适合深入挖掘问题的解决方案,而BFS则适用于逐层分析问题,寻找最短路径或按层次顺序进行操作。

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/690277
推荐阅读
相关标签
  

闽ICP备14008679号