欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

数据结构 图论5 DFS和BFS深度优先搜索和广度优先搜索详解 java

程序员文章站 2022-05-20 19:05:15
...

阅读前请先了解邻接矩阵

数据结构 图论5 DFS和BFS深度优先搜索和广度优先搜索详解 java

DFS深度优先搜索

DFS(Deep First Search),递归最深度访问其所有的临近节点(类似二叉树先序遍历);

比如A节点的临近节点就是C,D;C的临近节点就是A,B;

访问临近节点的优先级是A --> B --> C --> D --> E --> F,比如说A有C,D2个临近节点就会先访问C节点;

访问过的节点不再访问(通过visited标记)


例如:下面以A节点为起始点

  • 访问A节点,然后按照优先级访问C节点
  • C节点的邻近节点A已经visited,访问B节点
  • B节点D邻近节点C已经visited,B没有可以访问的节点,回退到A节点(类似二叉树先序遍历);
  • A节点向D节点访问
  • D节点 的 A,C邻近节点都已经visited,访问E节点
  • E节点的D邻近节点已经visited,访问F节点


注意这里访问并没有结束,因为我们还有很多分支没有遍历,优先级 “A --> B --> C --> D --> E --> F”,所以我们按照原路返回。(类似先序遍历)

  • 回退到E节点,E节点的所有邻近节点都visited
  • 回退到D节点,(检查F节点,不是邻近节点)D节点的所有邻近节点都visited
  • 回退到A节点,(检查E,F节点,不是邻近节点)A节点的所有邻近节点都visited

DFS结束 输出的顺序依次是 A --> C --> B --> D --> E --> F (0 - 2 - 1 - 3 - 4 - 5 )




代码

public class DFS {
    //A, B, C,D, E, F
    static int[][] graph = new int[][]{
            {0, 0, 1, 1, 0, 0},
            {0, 0, 1, 0, 0, 0},
            {1, 1, 0, 0, 0, 0},
            {0, 0, 1, 0, 1, 0},
            {0, 0, 0, 1, 0, 1},
            {0, 0, 0, 0, 1, 0}};
    static int[] visited = new int[graph.length];//用来记录已经遍历过的元素
    //DFS(深度优先遍历)同样适用于有向图 A->C->B->D->E->F 即 0->2->1->3->4->5
    public static void  dfsTraversing(int node, int[][] graph) {
        //遍历输出
        visited[node]=1;
        System.out.println(node);
        //循环一个长度
        for (int i = 0; i < graph[node].length; ++i) {
            //visited = 0;就是这个节点没有visited
            //i != node 就是传入的visited节点不是node,我们需要以node节点的下一个节点为基础,DFS
            //graph[node][i] == 1 保证这个node节点的下一个节点是可以通行的
            if (visited[i]==0&&i != node&&graph[node][i]==1) {
                dfsTraversing(i, graph);
            }
        }
    }

}





BFS 广度优先遍历

广度优先遍历类似树的层序遍历,比如我们将一个图按照起始点为A展开。

先遍历第一层,A;然后遍历第二层 C ,D;然后遍历第三层 B,E,最后遍历第四层 F。

数据结构 图论5 DFS和BFS深度优先搜索和广度优先搜索详解 java




利用队列来实现

数据结构 图论5 DFS和BFS深度优先搜索和广度优先搜索详解 java

每次poll出一个节点就将这个节点没有visited过的邻近点offer推入队列(比如A出队列,C,D就如队列)

入队列Queue的时候输出节点




例如:下面以A为起始节点

  • A入队列。输出A (queue={A})
  • A出队列 ,A的没有visited的邻近节点 C ,D入队列。输出C,D;(queue={C,D})
  • C出队列,C周围没有visited的B节点入队列;D出队列,D周围没有visited的节点E入队列。输出B,E;(queue={B,E})
  • B出队列,B周围都已经visited;E出队列E周围没有visited的F入队列。输出F;(queue={F})
  • F出队列,B周围都已经visited,且队列里面没有值,循环结束,程序结束



代码

public static void bfs(int[][] graph,int firstNode){
        LinkedList<Integer> queue = new LinkedList<>();
        int length = graph.length;
        System.out.println(firstNode);
        visited[firstNode] = true;
        queue.offer(firstNode);
        while (queue.size() > 0){
            //poll出一个值
            int node = queue.poll();
            //遍历这个行
            for (int i = 0; i < length; i++) {
                //如果行中有可以通行的点i
                //并且这个通行的点i没visited
                if (graph[node][i] != 0 && visited[i] == false) {
                    System.out.println(i);
                    //标记visited
                    visited[i] = true;
                    //就将这个点i入队列
                    queue.offer(i);
                }
            }
        }
    }






总代码

public class DfsAndBfs {
    /**
     * A,B,C,D,E,F
     * 0,1,2,3,4,5
     */
    private int[][] graph;
    /**
     * visited用来记录已经遍历过的元素
     */
    private boolean[] visited;

    public DfsAndBfs(int[][] graph) {
        this.graph = graph;
        visited = new boolean[graph.length];
    }
    /**
     * BFS(广度优先遍历) 基于队列实现
     */
    public  void bfs( int firstNode) {
        LinkedList<Integer> queue = new LinkedList<>();
        int length = graph.length;
        System.out.println(firstNode);
        visited[firstNode] = true;
        queue.offer(firstNode);
        while (queue.size() > 0) {
            //poll出一个值
            int node = queue.poll();
            //遍历这个行
            for (int i = 0; i < length; i++) {
                //如果行中有可以通行的点i
                //并且这个通行的点i没visited
                if (graph[node][i] != 0 && visited[i] == false) {
                    System.out.println(i);
                    //标记visited
                    visited[i] = true;
                    //就将这个点i入队列
                    queue.offer(i);
                }
            }
        }
    }

    /**
     * DFS 深度优先遍历 递归实现
     * (也可通过栈实现,因为递归的本质就是压栈)
     */
    public  void dfs(int node) {
        //遍历输出
        visited[node] = true;
        System.out.println(node);
        //循环一个长度
        for (int i = 0; i < graph[node].length; ++i) {
            //visited = false;就是这个节点没有visited
            //i != node 就是传入的visited节点不是node,我们需要以node节点的下一个节点为基础,DFS
            //graph[node][i] == 1 保证这个node节点的下一个节点是可以通行的
            if (visited[i] == false && i != node && graph[node][i] == 1) {
                dfs(i);
            }
        }
    }

}

test

int[][] graph = new int[][]{
  {0, 0, 1, 1, 0, 0},
  {0, 0, 1, 0, 0, 0},
  {1, 1, 0, 0, 0, 0},
  {0, 0, 1, 0, 1, 0},
  {0, 0, 0, 1, 0, 1},
  {0, 0, 0, 0, 1, 0}};

//0 2 1 3 4 5
new DfsAndBfs(graph).dfs(0);
System.out.println("==========");
//因为遍历会改变visited的值,所以需要重新构造对象
//0 2 3 1 4 5
new DfsAndBfs(graph).bfs(0);

refrence:

图的搜索算法:BFS和DFS详解(Java实现) - 简书 (jianshu.com)