【图(2)】:图的广度优先遍历和深度优先遍历

 

目录

图的遍历

一、图的广度优先遍历(bfs)

二、图的深度优先遍历


图的遍历

给定一个图G和其中任意一个顶点v0,从v0出发,沿着图中各边访问图中的所有顶点,且每个顶点仅被遍历一次。"遍历"即对结点进行某种操作的意思。
在二叉树中,我们对树的最多的操作就是各种遍历,而二叉树的前、中、后序遍历都是深度优先遍历,层序遍历就是广度优先遍历。

而二叉树可以被视为一种特殊类型的图。具体来说,二叉树是一种特殊的有向无环图(Directed Acyclic Graph,DAG)。

在一棵二叉树中,每个节点最多有两个子节点,分别称为左子节点和右子节点。这种结构符合图的定义,其中节点之间通过边(或连接)相互关联。

注意:两个遍历均基于【图(1)】上篇博客的邻接矩阵实现。


一、图的广度优先遍历(bfs)

类似于树,我们从起始顶点开始,同样一层一层遍历:

还是用这个简单图举例: 

假设以B顶点开始广度优先遍历:

 结合邻接矩阵我们可以清晰得出结论:每遍历一层顶点后,下一层需要遍历的顶点为该层顶点能指向的所有顶点。

下面模拟一下过程(与树的层序遍历相同,需要一个队列):

  1. 从B开始,遍历矩阵B对应的这一行,权值不为0的即B指向的顶点(A,C),将它们入队。
  2. A,C入队,对A,C进行相同的操作(poll)。而遍历A这一行发现A又指向了B,但B已经访问过,不能再次访问,因此在遍历前我们需要一个数组来记录每个顶点是否被访问过。再看A指向D,将D入队。
  3. poll出C,遍历C这一行,发现B,D都已经访问过,不需要操作。
  4. poll出D,同上。得到结果:B->A->C->D

综上,只要对二叉树的各种遍历非常熟悉(这里主要是层序遍历),然后刷过一些层序遍历、递归回溯的算法,图的广度优先遍历就非常简单了。

    /**
     * 图的广度优先遍历
     *
     * @param v 开始遍历的顶点
     */
    public void bfs(char v) {
        //1.定义一个数组标记当前顶点是否已经访问过
        boolean[] visited = new boolean[arrayV.length];
        //2.申请队列进行bfs
        Queue<Integer> queue = new ArrayDeque<>();
        int index = getIndexOfV(v); //记录当前顶点下标,也对应要遍历的行数
        queue.offer(index);
        visited[index] = true; //v位置设置为true

        while (!queue.isEmpty()) {
            index = queue.poll();
            System.out.print(arrayV[index] + " ");

            //遍历矩阵的第index行
            for (int i = 0; i < arrayV.length; i++) {
                //当前有连通节点且visited为false才能入队
                if (matrix[index][i] != 0 && !visited[i]) {
                    queue.offer(i);
                    visited[i] = true;
                }
            }
        }
    }

测试(基于上篇博客的邻接矩阵类):

    public static void main(String[] args) {
        GraphByMatrix graphByMatrix = new GraphByMatrix(4, false);
        char[] array = {'A', 'B', 'C', 'D'};
        graphByMatrix.initArrayV(array);

        graphByMatrix.addEdge('A', 'B', 1);
        graphByMatrix.addEdge('A', 'D', 1);
        graphByMatrix.addEdge('B', 'A', 1);
        graphByMatrix.addEdge('B', 'C', 1);
        graphByMatrix.addEdge('C', 'B', 1);
        graphByMatrix.addEdge('C', 'D', 1);
        graphByMatrix.addEdge('D', 'A', 1);
        graphByMatrix.addEdge('D', 'C', 1);

        //graphByMatrix.printGraph();

        System.out.println("顶点A的度:" + graphByMatrix.getDevOfV('A'));

        System.out.print("bfs: ");
        graphByMatrix.bfs('B');
        System.out.println();
        System.out.print("dfs: ");
        graphByMatrix.dfs('B');
    }

 

二、图的深度优先遍历(dfs)

图例: 

 还是来看前面那个简单的图:

总结:每次访问该顶点指向的第一个顶点,直到最后一个顶点指向的所有顶点都被访问过,开始回溯。

下面还是模拟一下过程:

  1. 从顶点B开始,遍历B这一行,发现B指向A,递归到A这一层再访问。
  2. 同样遍历A这一行,发现A又指向B了,那我们肯定不能进入这一层(回溯算法中的剪枝操作)。因此再遍历的同时同样需要一个标记数组来记录顶点是否被访问过。再继续遍历A,发现A指向D,D没被访问过,再递归进入D这一层。
  3. 遍历D这一行,D指向A,剪枝;继续遍历,D指向C,C没被访问过,再递归进入C这一层。
  4. 遍历C,发现C所有指向的顶点都已经被访问过。遍历结束后开始回溯。
  5. 回溯过程中,发现所有顶点都已经访问过,即都会被剪掉,因此再不会进入任何的递归操作。直到回到最开始的B这一层,循环结束,遍历完成。顺序:B->A->D->C。
    /**
     * 图的深度优先遍历
     *
     * @param v 起始顶点
     */
    public void dfs(char v) {
        visited = new boolean[arrayV.length];
        int index = getIndexOfV(v);
        dfsChild(index);
    }

    boolean[] visited; //记录当前顶点是否被访问过

    private void dfsChild(int index) {
        System.out.print(arrayV[index] + " ");
        visited[index] = true;
        //看矩阵中index这行, 是否还有未访问过的顶点
        for (int i = 0; i < arrayV.length; i++) {
            if (matrix[index][i] != 0 && !visited[i]) {
                dfsChild(i);
            }
        }
    }

测试(基于上篇博客的邻接矩阵类):

    public static void main(String[] args) {
        GraphByMatrix graphByMatrix = new GraphByMatrix(4, false);
        char[] array = {'A', 'B', 'C', 'D'};
        graphByMatrix.initArrayV(array);

        graphByMatrix.addEdge('A', 'B', 1);
        graphByMatrix.addEdge('A', 'D', 1);
        graphByMatrix.addEdge('B', 'A', 1);
        graphByMatrix.addEdge('B', 'C', 1);
        graphByMatrix.addEdge('C', 'B', 1);
        graphByMatrix.addEdge('C', 'D', 1);
        graphByMatrix.addEdge('D', 'A', 1);
        graphByMatrix.addEdge('D', 'C', 1);

        //graphByMatrix.printGraph();

        System.out.println("顶点A的度:" + graphByMatrix.getDevOfV('A'));

        System.out.print("bfs: ");
        graphByMatrix.bfs('B');
        System.out.println();
        System.out.print("dfs: ");
        graphByMatrix.dfs('B');
    }

 

最近更新

  1. TCP协议是安全的吗?

    2024-03-12 07:50:04       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-12 07:50:04       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-12 07:50:04       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-12 07:50:04       20 阅读

热门阅读

  1. C# chart曲线控件专题

    2024-03-12 07:50:04       19 阅读
  2. 17.8.1 InnoDB 启动配置

    2024-03-12 07:50:04       18 阅读
  3. 正则表达式

    2024-03-12 07:50:04       24 阅读
  4. VUE+内置iframe传值失效问题解决

    2024-03-12 07:50:04       24 阅读
  5. 嵌入式面经-数据结构-十大排序

    2024-03-12 07:50:04       22 阅读
  6. Android中类加载机制

    2024-03-12 07:50:04       24 阅读
  7. 前端自带的base64转化方法

    2024-03-12 07:50:04       21 阅读
  8. 2、设计模式之单例模式详解

    2024-03-12 07:50:04       20 阅读
  9. android JNI float *转MutableList

    2024-03-12 07:50:04       22 阅读
  10. ArrayList与LinkedList的区别

    2024-03-12 07:50:04       28 阅读
  11. django中的QuerySet

    2024-03-12 07:50:04       19 阅读