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

/**
     * 广度优先遍历
     * @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);
            }
        }
    }
}

结语:

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

护眼台灯哪个牌子好?好用又不伤眼的护眼台灯推荐

现在的孩子近视率非常高&#xff0c;如今不少低年级的孩子都已经存在近视的现象&#xff0c;究其原因主要还是学业压力过大、用眼时间过长、以及不合适的光源环境导致的。其中尤为注意孩子学习时书桌上的那一盏台灯&#xff0c;因为很多低质量的台灯在光源控制方面存在较大问题…

三防平板丨手持工业平板丨ONERugged工业三防平板丨推动数字化转型

随着科技的发展&#xff0c;数字化转型已经成为企业转型升级的必由之路。而在数字化转型中&#xff0c;三防平板作为一种重要的工具&#xff0c;可以极大地推动企业的数字化转型。本文将从以下几个方面探讨三防平板如何推动数字化转型。 一、提高工作效率 ONERugged加固平板的…

Vue+OpenLayers7入门到实战:OpenLayers加载google街景地图瓦片到地图上

返回《Vue+OpenLayers7》专栏目录:Vue+OpenLayers7 前言 本章介绍如何使用OpenLayers7在地图上加载google街景地图瓦片,无需科学上网,也可以正常访问瓦片。 二、依赖和使用 "ol": "7.5.2"使用npm安装依赖npm install ol@7.5.2使用Yarn安装依赖yarn a…

【Java】数据类型与变量

1.数据类型 在Java中数据类型主要分为两类&#xff1a;基本数据类型和引用数据类型。 基本数据类型有四类八种&#xff1a; 四类&#xff1a;整型、浮点型、字符型以及布尔型八种&#xff1a; 注意&#xff1a;不论是在16位系统还是32位系统&#xff0c;int都占用4个字节&am…

【Go-Zero】goctl生成model层后报错Unresolved reference ‘ErrNotFound‘解决方案

【Go-Zero】goctl生成model层后报错Unresolved reference ErrNotFound’解决方案 大家好 我是寸铁&#x1f44a; 总结了一篇goctl生成model层后报错Unresolved reference ErrNotFound’报错解决方案的文章✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 问题背景 大家好&#xff…

【医学大模型 补全主诉】BioGPT + LSTM 自动补全医院紧急部门主诉

BioGPT LSTM 自动补全医院紧急部门主诉 问题&#xff1a;针对在紧急部门中自动补全主诉的问题子问题1: 提高主诉记录的准确性子问题2: 加快主诉记录的速度子问题3: 统一医疗术语的使用子问题4: 减少打字错误和误解子问题5: 提高非特定主诉的处理能力 解法数据预处理神经网络方…

centos中安装go

安装过程 &#xff08;1&#xff09;源码二进制下载地址 wget https://dl.google.com/go/go1.13.5.linux-amd64.tar.gz &#xff08;2&#xff09;将下载的二进制包解压至 /usr/local目录。 tar -C /usr/local/ -xzf go1.13.5.src.tar.gz &#xff08;3&#xff09;设置环…

《基于CEEMDAN一小波包自适应阈值混凝土声发射信号降噪研究》算法思路笔记

![1]杨智中,林军志,汪魁等.基于CEEMDAN-小波包自适应阈值混凝土声发射信号降噪研究[J].振动与冲击,2023,42(03):139-149.DOI:10.13465/j.cnki.jvs.2023.03.016.](https://img-blog.csdnimg.cn/direct/9814ff64cc474cd3aa06ecaea60f2f75.png) 首先对周期循环荷载作用下混凝土试…

Cohere AI模型介绍

[Cohere AI模型介绍] Cohere有多种模型&#xff0c;涵盖许多不同的用例。如果需要更多自定义&#xff0c;可以训练模型以根据特定用例进行调整。 1. Command Command 是 Cohere 的默认生成模型&#xff0c;它接受用户指令&#xff08;或命令&#xff09;并按照指令生成文本。…

mkv和mp4什么区别?这篇文章告诉你(2024最新)

在数字视频的世界中&#xff0c;MKV和MP4是两种极为常见的封装格式。它们各自拥有独特的特点和优势&#xff0c;适用于不同的场景和需求。然而&#xff0c;对于许多用户来说&#xff0c;这两种格式之间的区别可能并不清晰。mkv和mp4什么区别&#xff1f;本文将探讨MKV和MP4之间…

CSS学习(三)

目录&#xff1a; 1. CSS引入方式 1.1 三种样式表 1.2 内部样式表&#xff08;嵌入式引入&#xff09; 1.3 行内样式表&#xff08;内联样式表&#xff09; 1.4 外部样式表 1.5 总结 1. CSS引入方式 1.1 三种样式表 1.2 内部样式表&#xff08;嵌入式引入&#xff09; …

知识蒸馏实战代码教学二(代码实战部分)

一、上章原理回顾 具体过程&#xff1a; &#xff08;1&#xff09;首先我们要先训练出较大模型既teacher模型。&#xff08;在图中没有出现&#xff09; &#xff08;2&#xff09;再对teacher模型进行蒸馏&#xff0c;此时我们已经有一个训练好的teacher模型&#xff0c;所以…

每日一题——LeetCode1464.数组中两元素的最大乘积

这题就是找数组里的最大值和次大值 方法一 排序 var maxProduct function(nums) {nums.sort((a,b)>b-a)return (nums[0] - 1) * (nums[1] - 1); }; 消耗时间和内存情况&#xff1a; 方法二 一次遍历&#xff1a; var maxProduct function(nums) {let first-1,second-…

IO进程线程第三天

1.使用fread,fwrite完成两个文件之间的拷贝 #include <myhead.h> //使用fread,fwrite完成两个文件之间的拷贝 int main(int argc, const char *argv[]) {if(argc!3){printf("input file error\n");printf("usage:./a.out srcfile destfile\n");retu…

Eclipse - Makefile generation

Eclipse - Makefile generation References right mouse click on the project -> Properties -> C/C Build -> Generate Makefiles automatically 默认会在 Debug 目录下创建 Makefile 文件。 References [1] Yongqiang Cheng, https://yongqiang.blog.csdn.net/

智慧城市驿站:智慧公厕升级版,打造现代化城市生活的便捷配套

随着城市化进程的加速&#xff0c;人们对城市生活质量的要求也越来越高。作为智慧城市建设的一项重要组成部分&#xff0c;多功能城市智慧驿站应运而生。它集合了信息技术、设计美学、结构工艺、系统集成、环保节能等多个亮点&#xff0c;将现代科技与城市生活相融合&#xff0…

文献速递:GAN医学影像合成--联邦生成对抗网络基础医学图像合成中的后门攻击与防御

文献速递&#xff1a;GAN医学影像合成–联邦生成对抗网络基础医学图像合成中的后门攻击与防御 01 文献速递介绍 虽然深度学习在医疗保健研究中产生了显著影响&#xff0c;但其在医疗保健领域的影响无疑比在其他应用领域更慢、更有限。造成这种情况的一个重要原因是&#xff…

uniapp不同平台获取文件内容以及base64编码特征

前言 文件图片上传&#xff0c;客户端预览是很正常的需求&#xff0c;获取文件的md5特征码也是很正常的&#xff0c;那么&#xff0c;在uniapp中三种环境&#xff0c;h5, 小程序以及 app环境下&#xff0c;如何实现的&#xff1f; 参考&#xff1a; 如何在uniapp中读取文件Arr…

自动驾驶TPM技术杂谈 ———— RFID(Radio Frequency IDentification)技术

文章目录 介绍问题挑战新机遇基于RFID 的绑定式感知基于标签信号物理模型基于标签能量耦合变化基于信号变化模式匹配 基于RFID 的非绑定式感知基于标签电感耦合基于反射信号模型基于信号模式匹配 基于RFID 的混合式感知 介绍 马克维瑟&#xff08;Mark Weiser&#xff09;在199…

vue+element (el-progress)标签 隐藏百分比(%) ,反向显示 ,自定义颜色, demo 复制粘贴拿去用

1 效果: 2 页面代码: <el-row :gutter"10" ><el-col :span"12"><el-card ><div class"fourqu"><div><span slot"title">{{推送任务TOP5}}</span></div></div><div class&…