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

news2025/1/19 7:56:03

目录

图的遍历概念:

图的广度优先遍历(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二维数组来存储顶点之间边的权重。

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

测试如下:

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

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

注意:

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

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

发生了DD的重复打印。

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

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

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

public static void main(String[] args) {
        GraphByMatrix graph = new GraphByMatrix(4,true);
        char[] array = {'A','B','C','D'};
        graph.initArrayV(array);
        graph.addEdge('A','B',1);
        graph.addEdge('A','D',1);
        graph.addEdge('B','A',1);
        graph.addEdge('B','C',1);
        graph.addEdge('C','B',1);
        graph.addEdge('C','D',1);
        graph.addEdge('D','A',1);
        graph.addEdge('D','C',1);
        graph.bfs('B');
    }

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

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

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

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

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

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

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

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

其过程如图所示:

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

代码实现如下:

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

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

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

测试如下:

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

总代码:

import java.sql.SQLOutput;
import java.util.Arrays;
import java.util.Queue;
import java.util.LinkedList;
public class GraphByMatrix {
    private char[] arrayV;//存放顶点·
    private int[][] matrix;//存放边
    private boolean isDirect;//是否是有向图
    public GraphByMatrix(int size,boolean isDirect){
        arrayV = new char[size];
        matrix = new int[size][size];
        for(int i = 0;i < size;i++){
            Arrays.fill(matrix[i],Integer.MAX_VALUE);
        }
        this.isDirect = isDirect;
    }
    /**
     * 初始化
     * @param array 顶点集合
     */
    public void initArrayV(char[] array){
        for(int i = 0;i < array.length;i++){
            arrayV[i] = array[i];
        }
    }

    /**
     *
     * @param v1 起始
     * @param v2 终点
     * @param weight 权值
     */
    public void addEdge(char v1,char v2,int weight){
        int index1 = getIndexOfV(v1);
        int index2 = getIndexOfV(v2);
        matrix[index1][index2] = weight;
        if(!isDirect){
            matrix[index2][index1] = weight;
        }
    }

    /**
     * 获取顶点元素在其数组中的下标
     * @param v
     * @return
     */
    public int getIndexOfV(char v){
        for(int i = 0;i < arrayV.length;i++){
            if(v == arrayV[i]){
                return i;
            }
        }
        return -1;
    }

    /**
     * 获取顶点的度
     * @param v
     * @return
     */
    public int getDevOfV(char v){
        int indexV = getIndexOfV(v);
        int count = 0;
        for(int i = 0;i < arrayV.length;i++){
            if(matrix[indexV][i] != Integer.MAX_VALUE){
                count++;
            }
        }
        if(isDirect){
            for(int i = 0;i < arrayV.length;i++){
                if(matrix[i][indexV] != Integer.MAX_VALUE){
                    count++;
                }
            }
        }
        return count;
    }
    public void printGraph(){
        for(int i = 0;i < arrayV.length;i++){
            System.out.print(arrayV[i] + " ");
        }
        System.out.println();
        for(int i = 0;i < matrix.length;i++){
            for(int j = 0;j < matrix[i].length;j++){
                if(matrix[i][j] == Integer.MAX_VALUE) {
                    System.out.print("∞ ");
                }else {
                    System.out.print(matrix[i][j]+" ");
                }
            }
            System.out.println();
        }
    }
    //广度优先遍历

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

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

结语:

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1458306.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTML 字符实体参考清单

前言 一些字符在 HTML 中是预留的&#xff0c;拥有特殊的含义&#xff0c;比如小于号‘<’用于定义 HTML 标签的开始。如果我们希望浏览器正确地显示这些字符&#xff0c;我们必须在 HTML 源码中插入字符实体。 字符实体有三部分&#xff1a;一个和号‘&’和一个实体名…

护眼台灯哪个牌子好?揭秘多款热销护眼台灯品牌

现在不管是学生党学习阅读&#xff0c;还是办公族加班工作&#xff0c;都离不开一盏光源舒适的台灯&#xff0c;然而如今的台灯市场水实在太深的&#xff0c;各种网红、劣质产品混杂在其中&#xff0c;一不留神就踩雷了。这类低质量的台灯往往采用劣质电源&#xff0c;其电源品…

怎么在抖音带自己的货?带货方式和带货要求,如下所示

我是王路飞。 不管你是无货源的新手小白&#xff0c;还是有货源的厂家/供应链&#xff0c;想在抖音卖货的话&#xff0c;无非就两种方式&#xff1a;要么开店、要么开直播带货。 看似都是在抖音卖货&#xff0c;但其实这是两条不同的赛道。 这篇文章就给你们聊下想在抖音卖货…

PTA——7-31 三角形判断

7-31 三角形判断 (15分) 给定平面上任意三个点的坐标(x​1​​,y​1​​)、(x​2​​,y​2​​)、(x​3​​,y​3​​)&#xff0c;检验它们能否构成三角形。 输入格式: 输入在一行中顺序给出六个[−100,100]范围内的数字&#xff0c;即三个点的坐标x​1​​、y​1​​、x​2​…

详解-领航家政策/双2.0模式

#领航家代理政策怎么代理# ∨&#xff1a;ok1234vip 简单点说&#xff01;费率/分润和返现先不说了&#xff0c;领航家是双2.0平台&#xff0c;用户也可以参与其中拼团&#xff0c;费率随之降低能一直降至0费率&#xff0c;甚至可以赚钱&#xff0c;&#xff08;这就是拼团两人…

OpenCV人脸检测案例实战

人脸检测是一种计算机视觉技术&#xff0c;旨在识别图像或视频中的人脸。这项技术的基本内容包括使用特定的算法和模型来定位和识别人脸&#xff0c;通常涉及在图像中寻找面部特征&#xff0c;如眼睛、鼻子、嘴巴等&#xff0c;以便准确地确定人脸的位置和边界。人脸检测技术的…

构造分钟降水R01文件

格式为&#xff1a;四川省降水强度数据集 目的&#xff1a;主要练习提取降水强度&#xff0c;而创建随机的分钟降水文件。 处理&#xff1a; 雨量筒降水不需要&#xff0c;统一处理为666666。 无降水与缺测&#xff08;标志2&#xff0c;3&#xff09;增加出现概率&#xf…

.NET 9 首个预览版发布:瞄准云原生和智能应用开发

前言 前不久.NET团队发布了.NET 9 的首个预览版&#xff0c;并且分享.NET团队对 .NET 9 的初步愿景&#xff0c;该愿景将于今年年底在 .NET Conf 2024 上发布。其中最重要的关注领域是&#xff1a;云原生和智能应用开发。 云原生开发人员平台 过去几年&#xff0c;.NET团队一…

在四维轻云中,如何实现地理空间数据云管理?

四维轻云是一款轻量化的地理空间数据网页管理平台&#xff0c;支持倾斜模型(.osgb)、激光点云(.las)、正射影像(dom)和数字高程模型(dem)等多种地理空间数据的在线管理、编辑及分享&#xff0c;其他类型地理空间数据也将陆续上线。 目前&#xff0c;平台具有项目管理、数据上传…

苹果打破App Store垄断,允许第三方应用商店存在 /马斯克的Neuralink首次成功植入芯片 |魔法半周报

我有魔法✨为你劈开信息大海❗ 高效获取AIGC的热门事件&#x1f525;&#xff0c;更新AIGC的最新动态&#xff0c;生成相应的魔法简报&#xff0c;节省阅读时间&#x1f47b; &#x1f525;资讯预览 苹果打破App Store垄断&#xff0c;允许第三方应用商店存在&#xff0c;但开…

关于SQL的各种Join你知道多少?

SQL中的 join&#xff0c;无外乎 inner join、outer join 以及 cross join&#xff0c;而 inner join 其实就是我们熟知的 join&#xff0c;outer join 其实就是 left outer join、right outer join 和 full outer join。 inner join 通过连接键列中的值进行匹配&#xff0c;…

深入浅出JVM(二)之运行时数据区和内存溢出异常

Java虚拟机在运行Java程序时,把所管理的内存分为多个区域, 这些区域就是运行时数据区 运行时数据区可以分为:程序计数器,Java虚拟机栈,本地方法栈,堆和方法区 程序计数器 Program Counter Register 程序记数寄存器 什么是程序计数器? 程序计数器是一块很小的内存,它可以当作…

LeetCode 0589.N 叉树的前序遍历:深度优先搜索(DFS)

【LetMeFly】589.N 叉树的前序遍历&#xff1a;深度优先搜索(DFS) 力扣题目链接&#xff1a;https://leetcode.cn/problems/n-ary-tree-preorder-traversal/ 给定一个 n 叉树的根节点 root &#xff0c;返回 其节点值的 前序遍历 。 n 叉树 在输入中按层序遍历进行序列化表…

灵活的数据权限思路

1、 前言 我一年java&#xff0c;在小公司&#xff0c;当前公司权限这块都没有成熟的方案&#xff0c;目前我知道权限分为功能权限和数据权限&#xff0c;我不知道数据权限这块大家是怎么解决的&#xff0c;但在实际项目中我遇到数据权限真的复杂&#xff0c;你永远不知道业主…

扫盲:什么是webGPU,和webGL对比哪些优点?

web端的3D图像渲染&#xff0c;大都采用webGL&#xff0c;不过其性能让大家很崩溃&#xff0c;webGPU的出现&#xff0c;让大家看到了访问加速的可能&#xff0c;本文通过对比webGPU与webGL&#xff0c;给老铁们普及一下。老铁们如有数据可视化的设计和开发需求&#xff0c;可以…

【Linux取经路】文件系统之重定向的实现原理

文章目录 一、再来理解重定向1.1 输出重定向效果演示1.2 重定向的原理1.3 dup21.4 输入重定向效果演示1.5 输入重定向代码实现 二、再来理解标准输出和标准错误2.1 同时对标准输出和标准错误进行重定向2.2 将标准输出和标准错误重定向到同一个文件 三、再看一切皆文件四、结语 …

代码随想录算法训练营day17||二叉树part04、110.平衡二叉树 、257. 二叉树的所有路径 、404.左叶子之和

注意&#xff1a;迭代法&#xff0c;可以先过&#xff0c;二刷有精力的时候 再去掌握迭代法。 110.平衡二叉树 &#xff08;优先掌握递归&#xff09; 再一次涉及到&#xff0c;什么是高度&#xff0c;什么是深度&#xff0c;可以巩固一下。 题目&#xff1a;给定一个二叉树&am…

Error creating bean with name ‘formContentFilter‘ defined in class path

问题描述 运行之前能正常的项目出现以上报错&#xff0c;提示创建“formContentFilter”时错误&#xff1b;org.springframework.boot版本2.4.8 org.springframework.beans.factory.BeanCreationException. Message: Error creating bean with name formContentFilter define…

Hadoop-Yarn-调度器总结

一、Yarn有哪些调度器 在cdh中Yarn组件中查看配置如下&#xff1a; 可以看到Yarn有三种调度器&#xff0c;分别是FairScheduler、FifoScheduler、CapacityScheduler&#xff0c;它们都是Hadoop的一个可插入调度器。 cdh默认的调度器是FairScheduler&#xff0c;hadoop默认的调…

Process Explorer下载安装使用教程(图文教程)超详细

「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 Process Explore 是微软的一款「进程资源管理器」&#xff0c;比Windows系统自带的任务管…