当前位置:   article > 正文

图的遍历(广度优先遍历BFS,深度优先遍历DFS)

图的遍历(广度优先遍历BFS,深度优先遍历DFS)

目录

图的遍历概念:

图的广度优先遍历(BFS):

代码实现如下:

测试如下:

注意:

图的深度优先遍历(DFS):

代码实现如下:

测试如下:

总代码:

结语:


图的遍历概念:

给定一个图G和其中任意一个顶点v0,从v0出发,沿着图中各边访问图中的所有顶点,且每个顶点仅被遍历一次。"遍历"即对结点进行某种操作的意思。由于考试大多考邻接矩阵(GraphByMatrix),故下面的遍历都是用邻接矩阵(GraphByMatrix),不是邻接表(GraphByNode)。

图的广度优先遍历(BFS):

广度优先遍历类似于我们前面所学二叉树的层序遍历,一层一层的走,故可以使用队列来模拟实现。

比如:现在有三个抽屉(每个抽屉包含一个红色盒子,红色盒子中又包含一个绿色盒子),所需东西在那个抽屉不清楚,现在要将其找到,广度优先遍历的做法是:

(1)先将三个抽屉打开,在最外层找一遍。

(2)将每个抽屉中红色的盒子打开,再找一遍。

(3)最后将红色盒子中绿色盒子打开,再找一遍。

直到找完所有的盒子,注意:每个盒子只能找一次,不能重复找。

例如下图:

该图的广度优先遍历过程如下:

故其广度优先遍历的结果为:ABCDEFGHI。

代码实现如下:

1、初始化一个布尔类型数组visited,默认所有顶点都没有被遍历到。

2、获取当前开始的顶点V 的下标。

3、定义一个队列,存储当前需要遍历的顶点的下标。

4、取出当前队列的头部。

5、把当前的顶点的这一行都放到队列。

由于getIndexOfV,arrayV,matrix在上一篇文章中已经非常详细的描述过,故这里我只解释其作用,如若需要源码和更加详细的解释请友友前往:图的存储结构 

(1)geiIndexOfV 获取顶点元素在其数组中的下标 。

(2)arrayV 顶点元素的一维数组。

(3)matrix 利用matrix二维数组来存储顶点之间边的权重。

  1. /**
  2. * 广度优先遍历
  3. * @param v
  4. */
  5. public void bfs(char v){
  6. //1、初始化一个布尔类型数组,默认所有顶点都没有被遍历到
  7. boolean[] visited = new boolean[arrayV.length];
  8. //2、获取当前开始的顶点V的下标
  9. int index = getIndexOfV(v);
  10. //3、定义一个队列,存储当前需要遍历的顶点的下标
  11. Queue<Integer> qu = new LinkedList<>();
  12. qu.offer(index);//起点放进来
  13. while(!qu.isEmpty()){
  14. //4、取出当前队列的头部
  15. int top = qu.poll();
  16. System.out.print(arrayV[top]+":"+"-> ");
  17. visited[top] = true;
  18. //5、把当前的顶点的这一行都放到队列
  19. for(int i = 0;i < arrayV.length;i++){
  20. //如果这一行的i下标不等于MAX_VALUE,并且也没有被访问过
  21. if(matrix[top][i] != Integer.MAX_VALUE && visited[i] == false){
  22. qu.offer(i);
  23. //注意,防止重复打印
  24. visited[i] = true;
  25. }
  26. }
  27. }
  28. System.out.println("null");
  29. }

测试如下:

测试代码均围绕下图进行:

遍历结果为BACD显然符合我们的预期。 

注意:

下面话红线的地方不能省去。

如若省去会发生重复遍历例如:

发生了DD的重复打印。

那为什么会发生重复打印呢?这是因为在C出队时,D已经在队列中了但是其还是false,故C出队会再次把D入队,这样就会重复打印。具体过程如下动图:

解决方法:在入队时一起把元素对应下标的visited数组设置为false。

为了方便友友调试下面将测试代码给出:

  1. public static void main(String[] args) {
  2. GraphByMatrix graph = new GraphByMatrix(4,true);
  3. char[] array = {'A','B','C','D'};
  4. graph.initArrayV(array);
  5. graph.addEdge('A','B',1);
  6. graph.addEdge('A','D',1);
  7. graph.addEdge('B','A',1);
  8. graph.addEdge('B','C',1);
  9. graph.addEdge('C','B',1);
  10. graph.addEdge('C','D',1);
  11. graph.addEdge('D','A',1);
  12. graph.addEdge('D','C',1);
  13. graph.bfs('B');
  14. }

图的深度优先遍历(DFS):

图的深度优先遍历类似于前面所学二叉树的前序遍历,有路就走,走完没路了再回退,使用递归来实现。

比如:现在有三个抽屉(每个抽屉包含一个红色盒子,红色盒子中又包含一个绿色盒子),所需东西在那个抽屉不清楚,现在要将其找到,深度优先遍历的做法是:

(1)先将第一个抽屉打开,在最外层找一遍。

(2)将第一个抽屉中红色的盒子打开,在红色箱子里找一遍。

(3)将红色盒子中绿色盒子打开,在绿箱子里找一遍。

(4)递归查找剩余两个箱子。

深度优先遍历:将一个抽屉一次性遍历完(包括该抽屉中包含的小盒子),再去递归遍历其它盒子。

其过程如图所示:

其深度优先遍历结果为:ABEGCFDHI。

代码实现如下:

实现一个方法dfschild来进行递归,为什么不用dfs直接递归呢?这是因为如果直接把dfs递归哪visited会一直被开辟,堆上的内存占用太大,要把visited设置在dfs外面才行。

部分流程和前面所说的广度优先遍历类似,关于getIndexOfV,arrayV,matrix在广度优先遍历那已解释故这里不再过多描述。

  1. /**
  2. * 给定顶点,从顶点处开始进行深度优先遍历
  3. * @param v
  4. */
  5. public void dfs(char v){
  6. //1、初始化一个布尔类型数组,默认所有顶点都没有被遍历到
  7. boolean[] visited = new boolean[arrayV.length];
  8. //2、获取当前开始的顶点V 的下标
  9. int index = getIndexOfV(v);
  10. //3、开始从index位置进行深度遍历
  11. dfsChild(index,visited);
  12. System.out.print("null");
  13. }
  14. /**
  15. * 从index位置开始深度优先遍历
  16. * @param index
  17. * @param visited
  18. */
  19. private void dfsChild(int index,boolean[] visited){
  20. System.out.print(arrayV[index]+":"+"-> ");
  21. visited[index] = true;
  22. //当前index位置的,所有的连接点都在这一行
  23. for(int i = 0;i < arrayV.length;i++){
  24. //如果这一行的i下标不等于0,并且也没有被访问过
  25. if(matrix[index][i] != Integer.MAX_VALUE && visited[i] == false){
  26. dfsChild(i,visited);
  27. }
  28. }
  29. }

测试如下:

遍历结果为:BADC显然符合我们的预期。

总代码:

  1. import java.sql.SQLOutput;
  2. import java.util.Arrays;
  3. import java.util.Queue;
  4. import java.util.LinkedList;
  5. public class GraphByMatrix {
  6. private char[] arrayV;//存放顶点·
  7. private int[][] matrix;//存放边
  8. private boolean isDirect;//是否是有向图
  9. public GraphByMatrix(int size,boolean isDirect){
  10. arrayV = new char[size];
  11. matrix = new int[size][size];
  12. for(int i = 0;i < size;i++){
  13. Arrays.fill(matrix[i],Integer.MAX_VALUE);
  14. }
  15. this.isDirect = isDirect;
  16. }
  17. /**
  18. * 初始化
  19. * @param array 顶点集合
  20. */
  21. public void initArrayV(char[] array){
  22. for(int i = 0;i < array.length;i++){
  23. arrayV[i] = array[i];
  24. }
  25. }
  26. /**
  27. *
  28. * @param v1 起始
  29. * @param v2 终点
  30. * @param weight 权值
  31. */
  32. public void addEdge(char v1,char v2,int weight){
  33. int index1 = getIndexOfV(v1);
  34. int index2 = getIndexOfV(v2);
  35. matrix[index1][index2] = weight;
  36. if(!isDirect){
  37. matrix[index2][index1] = weight;
  38. }
  39. }
  40. /**
  41. * 获取顶点元素在其数组中的下标
  42. * @param v
  43. * @return
  44. */
  45. public int getIndexOfV(char v){
  46. for(int i = 0;i < arrayV.length;i++){
  47. if(v == arrayV[i]){
  48. return i;
  49. }
  50. }
  51. return -1;
  52. }
  53. /**
  54. * 获取顶点的度
  55. * @param v
  56. * @return
  57. */
  58. public int getDevOfV(char v){
  59. int indexV = getIndexOfV(v);
  60. int count = 0;
  61. for(int i = 0;i < arrayV.length;i++){
  62. if(matrix[indexV][i] != Integer.MAX_VALUE){
  63. count++;
  64. }
  65. }
  66. if(isDirect){
  67. for(int i = 0;i < arrayV.length;i++){
  68. if(matrix[i][indexV] != Integer.MAX_VALUE){
  69. count++;
  70. }
  71. }
  72. }
  73. return count;
  74. }
  75. public void printGraph(){
  76. for(int i = 0;i < arrayV.length;i++){
  77. System.out.print(arrayV[i] + " ");
  78. }
  79. System.out.println();
  80. for(int i = 0;i < matrix.length;i++){
  81. for(int j = 0;j < matrix[i].length;j++){
  82. if(matrix[i][j] == Integer.MAX_VALUE) {
  83. System.out.print("∞ ");
  84. }else {
  85. System.out.print(matrix[i][j]+" ");
  86. }
  87. }
  88. System.out.println();
  89. }
  90. }
  91. //广度优先遍历
  92. /**
  93. * 广度优先遍历
  94. * @param v
  95. */
  96. public void bfs(char v){
  97. //1、初始化一个布尔类型数组,默认所有顶点都没有被遍历到
  98. boolean[] visited = new boolean[arrayV.length];
  99. //2、获取当前开始的顶点V的下标
  100. int index = getIndexOfV(v);
  101. //3、定义一个队列,存储当前需要遍历的顶点的下标
  102. Queue<Integer> qu = new LinkedList<>();
  103. qu.offer(index);//起点放进来
  104. while(!qu.isEmpty()){
  105. //4、取出当前队列的头部
  106. int top = qu.poll();
  107. System.out.print(arrayV[top]+":"+"-> ");
  108. visited[top] = true;
  109. //5、把当前的顶点的这一行都放到队列
  110. for(int i = 0;i < arrayV.length;i++){
  111. //如果这一行的i下标不等于MAX_VALUE,并且也没有被访问过
  112. if(matrix[top][i] != Integer.MAX_VALUE && visited[i] == false){
  113. qu.offer(i);
  114. //注意,防止重复打印
  115. // visited[i] = true;
  116. }
  117. }
  118. }
  119. System.out.println("null");
  120. }
  121. //图的深度优先遍历
  122. /**
  123. * 给定顶点,从顶点处开始进行深度优先遍历
  124. * @param v
  125. */
  126. public void dfs(char v){
  127. //1、初始化一个布尔类型数组,默认所有顶点都没有被遍历到
  128. boolean[] visited = new boolean[arrayV.length];
  129. //2、获取当前开始的顶点V 的下标
  130. int index = getIndexOfV(v);
  131. //3、开始从index位置进行深度遍历
  132. dfsChild(index,visited);
  133. System.out.print("null");
  134. }
  135. /**
  136. * 从index位置开始深度优先遍历
  137. * @param index
  138. * @param visited
  139. */
  140. private void dfsChild(int index,boolean[] visited){
  141. System.out.print(arrayV[index]+":"+"-> ");
  142. visited[index] = true;
  143. //当前index位置的,所有的连接点都在这一行
  144. for(int i = 0;i < arrayV.length;i++){
  145. //如果这一行的i下标不等于0,并且也没有被访问过
  146. if(matrix[index][i] != Integer.MAX_VALUE && visited[i] == false){
  147. dfsChild(i,visited);
  148. }
  149. }
  150. }
  151. }

结语:

其实写博客不仅仅是为了教大家,同时这也有利于我巩固自己的知识点,和一个学习的总结,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进,如果大家有所收获的话还请不要吝啬你们的点赞收藏和关注,这可以激励我写出更加优秀的文章。

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号