[ 数据结构 ] 图(Graph)--------深度优先、广度优先遍历

news2025/1/24 1:20:38

0 基本介绍

  1. 为什么要有图?

    无论是线性表还是树结构,局限于表示一个直接前驱和一个直接后继的关系(一对一/一对多),当我们需要表示多对多的关系时, 这里我们就用到了图

  2. 节点间的连接成为边,节点称为顶点,一个顶点到另一个顶点所经过的边叫路径,边有方向的叫有向图,边没有方向的叫无向图,边带权值的叫带权图也叫网

  3. 图的表示方式有两种:邻接矩阵/邻接表,分别使用二维数组/链表,简单理解:顶点3所表示的一维数组/单链表中,顶点5表示的元素/节点大小就是顶点3和5间边的权值,权值为1表示可直接连接,否则无法直连(通常不用0而使用极大值65535表示)

image-20230109132754621.png

00000000000000000000000上图为邻接矩阵,下图为邻接表000000000000000000000000

image-20230109132856023.png

1 深度优先遍历

  1. 首先创建图Graph类,属性包括邻接矩阵(二维数组)、顶点集合、边总数
  2. 传入以上参数即可构造图对象
  3. 编写对单个顶点的深度优先遍历方法dfs:传参为顶点在集合中的下标index,以及表示是否访问顶点的数组isVisited,方法体为打印index顶点并更新其为已访问,获取index的首个未访问过的邻接节点w,对w顶点递归调用dfs,方法结束
  4. 考虑到对单个顶点深度优先遍历,可能会有深度不够的情况,因此循环对所有顶点调用dfs

image-20230109135715326.png

//封装调用dfs
    //虽是深度优先遍历,但考虑邻接中断(深度不够),因此起点有序调用dfs
    public void dfs() {
        boolean[] isVisited = new boolean[vertexes.size()];
        for (int i = 0; i < vertexes.size(); i++) {
            if (!isVisited[i]) {
                dfs(i,isVisited);
            }
        }
    }
    //深度优先遍历
    //对当前节点的处理(打印+已读)+向邻接结点递归
    public void dfs(int index, boolean[] isVisited) {
        System.out.print(getValueByIndex(index) + "->");
        isVisited[index] = true;
        int w = getFirstNeighbor(index);
        while (w != -1) {
            if (isVisited[w]) {
                w = getNextNeighbor(index, w);
            } else {
                dfs(w,isVisited);
            }
        }
    }

2 广度优先遍历

  1. 沿用1、2步,编写单个顶点的广度优先遍历方法bfs:对index顶点打印且标记为已访问,遍历其所有未访问过的邻接节点,并打印和加入队列
  2. 取出队列头部的顶点,同样遍历其所有未访问过的邻接节点,并打印和加入队列
  3. 5和6步骤采用内外两层while循环实现
  4. 考虑到对单个顶点广度度优先遍历,可能会有广度不够的情况,因此循环对所有顶点调用dfs
//封装调用bfs
    //虽是广度优先遍历,但考虑广度不够,因此起点有序调用dfs
    public void bfs() {
        boolean[] isVisited = new boolean[vertexes.size()];
        for (int i = 0; i < vertexes.size(); i++) {
            if (!isVisited[i]) {
                bfs(i,isVisited);
            }
        }
    }
    //广度优先遍历
    public void bfs(int index, boolean[] isVisited) {
        int u = 0;
        int w = 0;
        System.out.print(getValueByIndex(index) + "->");
        isVisited[index] = true;
        LinkedList<Integer> queue = new LinkedList<>();
        queue.addLast(index);

        while (!queue.isEmpty()) {
            //下一个节点的广度遍历
            u = queue.removeFirst();
            w = getFirstNeighbor(u);
            //遍历队列头的所有邻接节点
            while (w != -1) {
                if (!isVisited[w]) {
                    System.out.print(getValueByIndex(w) + "->");
                    isVisited[w] = true;
                    //填充队列
                    queue.addLast(w);
                }
                w = getNextNeighbor(u, w);
            }
        }
    }

3 代码实现

//图
public class Graph {
    public static void main(String[] args) {

        //测试
        int n = 5;
        Graph graph = new Graph(5);
        String[] vertexValue = {"A", "B", "C", "D", "E"};
        for (String s : vertexValue) {
            graph.insertVertex(s);
        }
        graph.insertEdge(1,0,1);
        graph.insertEdge(1,2,1);
        graph.insertEdge(1,3,1);
        graph.insertEdge(1,4,1);
        graph.insertEdge(2,0,1);
        graph.show();
        graph.dfs(0,new boolean[5]);
//        graph.bfs();
    }

    List<String> vertexes;
    int[][] edges;
    int edgesNum;

    public Graph(int n) {
        vertexes = new ArrayList<>(n);
        edges = new int[n][n];
        edgesNum = 0;
    }

    //封装调用bfs
    //虽是广度优先遍历,但考虑广度不够,因此起点有序调用dfs
    public void bfs() {
        boolean[] isVisited = new boolean[vertexes.size()];
        for (int i = 0; i < vertexes.size(); i++) {
            if (!isVisited[i]) {
                bfs(i,isVisited);
            }
        }
    }
    //广度优先遍历
    public void bfs(int index, boolean[] isVisited) {
        int u = 0;
        int w = 0;
        System.out.print(getValueByIndex(index) + "->");
        isVisited[index] = true;
        LinkedList<Integer> queue = new LinkedList<>();
        queue.addLast(index);

        while (!queue.isEmpty()) {
            //下一个节点的广度遍历
            u = queue.removeFirst();
            w = getFirstNeighbor(u);
            //遍历队列头的所有邻接节点
            while (w != -1) {
                if (!isVisited[w]) {
                    System.out.print(getValueByIndex(w) + "->");
                    isVisited[w] = true;
                    //填充队列
                    queue.addLast(w);
                }
                w = getNextNeighbor(u, w);
            }
        }
    }

    //封装调用dfs
    //虽是深度优先遍历,但考虑邻接中断(深度不够),因此起点有序调用dfs
    public void dfs() {
        boolean[] isVisited = new boolean[vertexes.size()];
        for (int i = 0; i < vertexes.size(); i++) {
            if (!isVisited[i]) {
                dfs(i,isVisited);
            }
        }
    }
    //深度优先遍历
    //对当前节点的处理(打印+已读)+向邻接结点递归
    public void dfs(int index, boolean[] isVisited) {
        System.out.print(getValueByIndex(index) + "->");
        isVisited[index] = true;
        int w = getFirstNeighbor(index);
        while (w != -1) {
            if (isVisited[w]) {
                w = getNextNeighbor(index, w);
            } else {
                dfs(w,isVisited);
            }
        }
    }
    //获取第一个邻接结点
    public int getFirstNeighbor(int index) {
        for (int i = 0; i < vertexes.size(); i++) {
            if (edges[index][i] > 0) {
                return i;
            }
        }
        return -1;
    }
    //获取下一个邻接节点
    public int getNextNeighbor(int v1, int v2) {
        for (int i = v2+1; i < vertexes.size(); i++) {
            if (edges[v1][i] > 0) {
                return i;
            }
        }
        return -1;
    }

    //插入节点
    public void insertVertex(String vertex) {
        vertexes.add(vertex);
    }

    //插入边
    public void insertEdge(int v1, int v2, int weight) {
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        edgesNum++;
    }

    //返回节点数
    public int getVertexNum() {
        return vertexes.size();
    }

    //返回边个数
    public int getEdgesNum() {
        return edgesNum;
    }

    //返回节点
    public String getValueByIndex(int index) {
        return vertexes.get(index);
    }

    //返回边的权值
    public int getWeight(int v1, int v2) {
        return edges[v1][v2];
    }

    //显示图对应的矩阵
    public void show() {
        for (int[] edge : edges) {
            System.out.println(Arrays.toString(edge));
        }
    }
}

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

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

相关文章

js逆向-某动网演出数据获取

声明 本文仅供学习参考&#xff0c;如有侵权可私信本人删除&#xff0c;请勿用于其他途径&#xff0c;违者后果自负&#xff01; 如果觉得文章对你有所帮助&#xff0c;可以给博主点击关注和收藏哦&#xff01; 前言 目标网站&#xff1a;aHR0cHM6Ly93d3cuc2hvd3N0YXJ0LmNv…

如何远程连接Linux系统服务器

Linux服务器远程连接方法这里给大家普及一下Linux服务器&#xff0c;是一台安装Ubuntu系统的工作站。这类服务器大部分用于程序员开发编程使用&#xff0c;说简单点就是拿来敲代码的。通常需要借助远程连接工具来连接Linux远程服务器&#xff0c;如xshell&#xff1b;putty&…

PostgreSQL实战之物理复制和逻辑复制(五)

目录 PostgreSQL实战之物理复制和逻辑复制&#xff08;五&#xff09; 5 流复制主备切换 5.1 判断主备角色的五种方法 5.2 主备切换之文件触发方式 5.3 主备切换之pg_ctl promote方式 5.4 pg_rewind PostgreSQL实战之物理复制和逻辑复制&#xff08;五&#xff09; 5 流…

Vue作业

文章目录作业1作业2作业3作业4作业1 作业&#xff1a;需要用data保存&#xff1a;商品名、单价、数量&#xff0c;然后显示到页面上&#xff0c;点击按钮可以变更数量&#xff0c;最小值1&#xff0c;减按钮不可用&#xff0c;最大值20&#xff0c;隐藏按钮 总价格应该是 单价…

酒业“新物种”的新思维:用户、品牌、模式

【潮汐商业评论/原创】中国的酒文化源远流长。古人有一万种喝酒的理由&#xff0c;或聚会畅饮&#xff0c;或独酌解忧&#xff0c;而餐桌是酒最常出现的地方。如今&#xff0c;与酒相关的场景日益多元&#xff0c;往往洋溢着青春的气息。一顶帐篷&#xff0c;三两好友围坐&…

一、数据仓库基础理论

数据仓库基础理论一、数据仓库1、概念2、数据仓库分层结构3、为什么要分层二、数据集市三、数据湖1、数据湖和数据仓库一、数据仓库 1、概念 数据仓库&#xff08;Data Warehouse, DW&#xff09;&#xff1a;一个面向主题的、集成的、非易失的、反应历史变化的、用来支持企业…

哈弗品牌发布新能源越野新品类车型:H-DOG

1月6日&#xff0c;哈弗品牌新能源车型哈弗H-DOG在海口新能源车展亮相。哈弗H-DOG针对用户5天通勤、2天郊野、e享时刻三大使用场景打磨&#xff0c;满足用户“可城、可野、可电”的用车需求&#xff0c;是哈弗品牌在新能源轻越野上的全新探索。• 哈弗H-DOG采用了全新的潮野力量…

Sentinel降级策略-RT、异常比例、异常数

Sentinel降级—RT 1&#xff09;RT&#xff1a;平均响应时间&#xff1b;配置平均响应时间为200ms&#xff0c;200ms内就要将请求处理完成&#xff1b; 2&#xff09;注意&#xff1a;Sentinel 默认统计的 RT 上限是4900ms&#xff0c;超出此阈值会算作 4900ms &#xff0c;若…

【PyTorch深度学习实践】05_逻辑斯蒂回归

文章目录1. 分类问题相关数据集1.1 MINIST1.2 CIFAR-102. 回归(Regression)VS分类(Classification)2.1 模型对比2.2 损失函数对比2.3 实现代码对比3. 完整代码之前使用线性回归解决的都是**回归&#xff08;预测&#xff09;**问题&#xff0c;逻辑斯蒂回归模型可以用来解决另一…

MySQL数据库:数据基本的增删改查

一、查询数据 1.查询表内所有数据 select * from 表名; 2.指定列查询 select 字段1, 字段2, …… from 表名; 3.查询字段为表达式 select 表达式1, 表达式2,…… from 表名; 4.起表名查询 如果对查询结果的字段名不满意&#xff0c;还可以自己进行取别名。 select 字段1 …

#Reading Paper# 【序列推荐】Session-based Recommendation with Graph Neural Networks

#论文题目&#xff1a;【序列推荐】SR-GNN: Session-based Recommendation with Graph Neural Networks&#xff08;SR-GNN:基于会话的图神经网络推荐&#xff09; #论文地址&#xff1a;https://arxiv.org/abs/1811.00855 #论文源码开源地址&#xff1a; https://github.com/C…

如何设置Windows文件夹背景为黑色?(其实就是“深色模式”)

大家好。我们直接进入正题! 如何把Windows文件夹背景改成黑色&#xff1f;就像下面这样。 第一步&#xff1a;打开 “个性化” 设置界面 这里介绍两种方法&#xff1a;(1)在电脑桌面 右键——》 个性化 &#xff0c;如下图 (2)点击 开始——》设置 ——》个性化 &#xff0c;如…

Typora+upic的配置

typora upic 的配置 背景说明 本人使用的是Mac book 的M1芯片电脑&#xff0c;为了方便写博客&#xff0c;整理了图片上传的方式&#xff1b; upic是一款上传图片的工具&#xff0c;主要为了帮助写博客上传工具使用的&#xff0c;方便上传图片&#xff1b; 配置七牛云的图床…

如何在 Kubernetes 部署 PostgreSQL

文章目录1. 简介2. 条件3. helm 部署 posgresql3.1 添加 Helm 存储库3.2 默认安装3.3 选参安装3.4 持久存储安装3.4.1 创建 PersistentVolume3.4.2 创建 PersistentVolumeClaim3.4.3 安装 Helm Chart3.4.5 连接到 PostgreSQL 客户端3.5 自定义配置 value.yaml4. 手动部署 postg…

Anaconda、CUDA、Pytorch安装

文章目录Anaconda、CUDA、Pytorch安装安装Anaconda安装CUDA安装cuDNN安装Pytorch小技巧验证原文链接&#xff1a; Tommy Shang的博客Anaconda、CUDA、Pytorch安装 很久没有更新博客&#xff0c;最近给实验室的机器安装Pytorch环境&#xff0c;顺手也把自己的机器装了一遍。 整…

《凤凰项目》读后感

无极限零部件公司的问题不提变更单导致变更引起很多问题&#xff0c;变更登记系统推行不下去不知道自己团队有多少项目在运行导致无法预估人力&#xff0c;进度怎么样&#xff0c;项目管理系统推行不下去安全部门提出各种安全问题&#xff0c;补丁安装审计部门提出各种审计不合…

Java学习之代码块

目录 一、代码块的基本介绍 二、基本语法 注意事项 三、代码块的好处和案例演示 四、注意事项和使用细节 第一点 第二点 第三点 案例演示 第四点 第五点 第六点 第七点 五、练习题 第一题 第二题 考察知识点 结论 结果 一、代码块的基本介绍 代码化块又称为初…

【论文导读】Deep Stable Learning for Out-Of-Distribution Generalization

DWR用到复杂数据集中。 看了一半发现一个博客将的也不错 放在这里Deep Stable Learning for Out-Of-Distribution Generalization_六点先生的博客-CSDN博客_随机傅里叶特征 目标任务&#xff1a; 一般假设&#xff08;训练数据的已知异质性&#xff08;如领域标签&#xff0…

FGH75T65SHD-F155 场截止沟槽 IGBT单管 应用于太阳能逆变器、UPS等多种应用

FGH75T65SHD-F155采用新型场截止 IGBT 技术&#xff0c;为太阳能逆变器、UPS、焊接机、电信、ESS 和 PFC 等低导通和开关损耗至关重要的应用提供最佳性能。 ONsemi安森美IGBT单管系列&#xff1a; FGH40N60SMD FGH60N60SMD FGH75T65SHD-F155 NGTB40N120FL2WG 特性&#x…

架构设计---用户加密处理

前言&#xff1a; 在互联网各种安全问题中&#xff0c;最能引发话题&#xff0c;刺激大众神经的就是用户的泄密问题&#xff0c;数据库被拖库导致所有的数据泄露&#xff0c;这种系统安全问题涉及的因素可能有很多&#xff0c;大部分和开发软件的程序员没有关系&#xff0c;但…