【详解】图的概念和存储结构(邻接矩阵,邻接表)

目录

图的基本概念:

图的存储结构

邻接矩阵(GraphByMatrix):

基本参数:

初始化:

获取顶点元素在其数组中的下标 :

添加边和权重:

获取顶点的度:

打印图:

邻接表(GraphByNode):

基本参数:

注意:

初始化:

获取顶点元素在其数组中的下标 :

添加边和权重:

获取顶点的度:

打印图:

结语:


图的基本概念:

图是由顶点集合及顶点间的关系组成的一种数据结构:G = (V, E)。

其中:

(1)顶点集合V = {x|x属于某个数据对象集}是有穷非空集合。

(2)E = {(x,y)|x,y属于V}或者E = {|x,y属于V && Path(x, y)}是顶点间关系的有穷集合,也叫做边的集合。

(3)(x, y)表示x到y的一条双向通路,即(x, y)是无方向的;Path表示从x到y的一条单向通路,即Path 是有方向的。

顶点和边:

图中结点称为顶点,第i个顶点记作vi。两个顶点vi和vj相关联称作顶点vi和顶点vj之间有一条边, 图中的第k条边记作ek,ek = (vi,vj)或。

有向图和无向图:

在有向图中,顶点对是有序的,顶点对称为顶点x到顶点y的一条边(弧),和是两条不同的边,比如下图G3和G4为有向图。在无向图中,顶点对(x, y)是无序的,顶点对(x,y) 称为顶点x和顶点y相关联的一条边,这条边没有特定方向,(x, y)和(y,x)是同一条边,比如下图G1和G2为 无向图。注意:无向边(x, y)等于有向边和。

例如下图:G1和G2位无向图,G3和G4为有向图。

完全图:

在有n个顶点的无向图中,若有n * (n-1)/2条边,即任意两个顶点之间有且仅有一条边,则称此图为 无向完全图,比如上图G1;在n个顶点的有向图中,若有n * (n-1)条边,即任意两个顶点之间有且仅有方向 相反的边,则称此图为有向完全图,比如上图G4。 

顶点的度:

顶点v的度是指与它相关联的边的条数,记作deg(v)。在有向图中,顶点的度等于该顶点的入度与 出度之和,其中顶点v的入度是以v为终点的有向边的条数,记作indev(v);顶点v的出度是以v为起始点的有向 边的条数,记作outdev(v)。因此:dev(v) = indev(v) + outdev(v)。注意:对于无向图,顶点的度等于该顶 点的入度和出度,即dev(v) = indev(v) = outdev(v)。

路径:

在图G = (V, E)中,若从顶点vi出发有一组边使其可到达顶点vj,则称顶点vi到顶点vj的顶点序列为从顶点vi到顶点vj的路径。

路径长度:

对于不带权的图,一条路径的路径长度是指该路径上的边的条数;对于带权的图,一条路径的路 径长度是指该路径上各个边权值的总和。

简单路径与回路:

若路径上各顶点v1,v2,v3,…,vm均不重复,则称这样的路径为简单路径。若路 径上 第一个顶点v1和最后一个顶点vm重合,则称这样的路径为回路或环。例如下图:

子图:

设图G = {V, E}和图G1 = {V1,E1},若V1属于V且E1属于E,则称G1是G的子图。如下图:

连通图: 

在无向图中,若从顶点v1到顶点v2有路径,则称顶点v1与顶点v2是连通的。如果图中任意一 对顶点 都是连通的,则称此图为连通图。

强连通图:

在有向图中,若在每一对顶点vi和vj之间都存在一条从vi到vj的路径,也存在一条从vj到 vi的路 径,则称此图是强连通图。

生成树:

一个连通图的最小连通子图称作该图的生成树。有n个顶点的连通图的生成树有n个顶点和n-1条 边。

图的存储结构

因为图中既有节点,又有边(节点与节点之间的关系),因此,在图的存储中,只需要保存:节点和边关系即可。故图的存储结构有两种。1.邻接矩阵 2.邻接表。其中最常用的是邻接矩阵。

邻接矩阵(GraphByMatrix):

因为节点与节点之间的关系就是连通与否,即为0或者1,因此邻接矩阵(二维数组)即是:先用一个数组将定 点保存,然后采用矩阵来表示节点与节点之间的关系。

注意:

1. 无向图的邻接矩阵是对称的,第i行(列)元素之和,就是顶点i的度。有向图的邻接矩阵则不一定是对称的,第i行(列)元素之后就是顶点i 的出(入)度。

2. 如果边带有权值,并且两个节点之间是连通的,上图中的边的关系就用权值代替,如果两个顶点不通,则使用无穷大代替。

如下图:

3. 用邻接矩阵存储图的优点是能够快速知道两个顶点是否连通,缺陷是如果顶点比较多,边比较少时,矩 阵中存储了大量的0成为系数矩阵,比较浪费空间,并且要求两个节点之间的路径不是很好求。 

实现GraphByMatrix类,arrayv用来存放顶点,matrix来存放边,isDirect用来判断图是否是有向图。根据上面给出的注意2要用fill将数组初始化为无穷大。

基本参数:

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;}
}

初始化:

初始化arrayV顶点数组。

public void initArrayV(char[] array){for(int i = 0;i < array.length;i++){arrayV[i] = array[i];}}

获取顶点元素在其数组中的下标 :

public int getIndexOfV(char v){for(int i = 0;i < arrayV.length;i++){if(v == arrayV[i]){return i;}}return -1;}

添加边和权重:

先查找出两个顶点在,顶点数组中的位置,特别注意:无向图的话,两边都要设置,因为有向图每条边都是单独的。

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;}}

效果如下:

这是一个无向图,将边的关系抽象为二维数组,其中2^31-1为未赋予权重。

 

获取顶点的度:

1、现在顶点数组 arrayV 中找到顶点的下标。

2、无向图只需要计算出度就好了。

3、如果是有向图,有向图的度 = 入度 +出度。

4、此时的count中存储的就是顶点V的度。

第二个for循环是沿着y轴遍历。 

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;}

测试如下:

我们add了A的两条边故打印2.

 

打印图:

为了使打印效果更好我们将2^31-1打印为无穷大。

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();}}

效果如下:

邻接表(GraphByNode):

邻接表:使用数组表示顶点的集合,使用链表表示边的关系。

1. 无向图邻接表存储

注意:无向图中同一条边在邻接表中出现了两次。如果想知道顶点vi的度,只需要知道顶点vi边链表集 合中结点的数目即可。

2.有向图邻接表存储:

注意:有向图中每条边在邻接表中只出现一次,与顶点vi对应的邻接表所含结点的个数,就是该顶点的出度,也称出度表,要得到vi顶点的入度,必须检测其他所有顶点对应的边链表,看有多少边顶点的dst取值是i。

基本参数:

需要设置一个静态内部类Node结点,结点要能存储起始,终点,权重和下一结点的数据。运用数组加链表的存储方式。

public class GraphByNode {static class Node{public int src;//起始下标public int dest;//重点下标public int weight;//权重public Node next;public Node(int src,int dest,int weight){this.src = src;this.dest = dest;this.weight = weight;}}private ArrayList<Node> edgeList;private char[] arrayV;//存放顶点private boolean isDirect;//是否是有向图public GraphByNode(int size,boolean isDirect){arrayV = new char[size];edgeList = new ArrayList<>(size);for(int i = 0;i < size;i++){edgeList.add(null);}this.isDirect = isDirect;}
}
注意:

new ArrayList<>(size)里面直接加参数是不能初始化list大小的例如:

 

我们可以看到size的大小为0这样在进行操作时就会报错。 

解决方法如下:

这样就成功解决了💯。 

初始化:

public void initArrayV(char[] array){for(int i = 0;i < array.length;i++){arrayV[i] = array[i];}}

获取顶点元素在其数组中的下标 :

public int getIndexOfV(char v){for(int i = 0;i < arrayV.length;i++){if(arrayV[i] == v){return i;}}return -1;}

添加边和权重:

为了使代码更加整洁在addEdge里面再调用addEdgeChild方法。注意区分有向图和无向图的区别,如果要添加的边已经再链表里了直接return退出添加失败而不是大多数的覆盖,用头插法插入数据。

public void addEdge(char v1,char v2,int weight){int src = getIndexOfV(v1);int dest = getIndexOfV(v2);addEdgeChild(src,dest,weight);if(!isDirect){addEdgeChild(dest,src,weight);}}private void addEdgeChild(int src,int dest,int weight){Node cur = edgeList.get(src);while(cur != null){if(cur.dest == dest){return;}cur = cur.next;}//头插法Node node = new Node(src,dest,weight);node.next = edgeList.get(src);edgeList.set(src,node);}

效果如下:

获取顶点的度:

1、现在顶点数组 arrayV 中找到顶点的下标。

2、如果是无向图,只需要遍历链表的节点个数。

3、如果是有向图,必须检测其他所有顶点对应的边链表,看有多少边顶点的dst取值是i        //有向图也就一张表。

使用continue来跳过当前自己的顶点链表防止有重复。

public int getDevOfV(char v){int indexV = getIndexOfV(v);int count = 0;Node cur = edgeList.get(indexV);while(cur != null){count++;cur = cur.next;}if(isDirect){int dest = indexV;for(int src = 0;src < arrayV.length;src++){if(src == dest){continue;}else{Node pCur = edgeList.get(src);while(pCur != null){if(pCur.dest == dest){count++;}pCur = pCur.next;}}}}return count;}

效果如下:

打印图:

 public void printGraph(){for(int i = 0;i < arrayV.length;i++){System.out.print(arrayV[i] + "->");Node cur = edgeList.get(i);while(cur != null){System.out.print(cur.dest + "->");cur = cur.next;}System.out.println("null");}}

效果如下:

结语:

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

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

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

相关文章

Unity所有关于旋转的方法详解

前言&#xff1a;欧拉角和四元数的简单描述 我们在Inspector面板上看到的rotation其实是欧拉角&#xff0c; 我们将Inspector面板设置成Debug模式&#xff0c;此时看到的local Rotation才是四元数。 Unity中的欧拉旋转是按照Z-X-Y顺规执行的旋转&#xff0c;一组欧拉旋转过程中…

【HarmonyOS】鸿蒙Arkts开发从入门到实战【持续更新】

鸿蒙Arkts开发从入门到实战目录 1. 工具安装 ArkTs 2.1 基础语法 2.2 状态管理 2.3 渲染控制 2.4 页面路由 ArkUI的基础类组件使用 3.1 Image组件 3.2 Text组件 3.3 TextInput组件 3.4 Button组件 3.5 Slider组件 3.6 Video组件 3.7 自定义组件 3.8 气泡提示 3.9 菜单 Stage模…

RibbonBar RibbonPage切换事件

在开发的过程中&#xff0c;我们会用到点击切换page&#xff0c;来响应对应的事件&#xff0c;例如以下事件&#xff1a; 头文件中&#xff1a; void ribboncurrentPageIndexChanged(int index); 实现文件中&#xff1a; connect(ribbonBar(), SIGNAL(currentPageIndexChang…

[力扣 Hot100]Day29 删除链表的倒数第 N 个结点

题目描述 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 出处 思路 两个指针间隔n&#xff0c;一趟遍历解决。 代码 class Solution { public:ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode* phead;ListNode* …

【数据库】Mysql索引

1、什么是索引&#xff1f;为什么要用索引&#xff1f; 1.1、索引的含义 数据库索引&#xff0c;是数据库管理系统中一个排序的数据结构&#xff0c;以协助快速查询&#xff0c;更新数据库中表的数据。索引的实现通常使用B树和变种的B树&#xff08;MySQL常用的索引就是B树&am…

跟着pink老师前端入门教程(JavaScript)-day03

四、常量 概念&#xff1a;使用 const 声明的变量称为“常量”。 使用场景&#xff1a;当某个变量永远不会改变的时候&#xff0c;就可以使用 const 来声明&#xff0c;而不是let。 命名规范&#xff1a;和变量一致 常量使用&#xff1a; 注意&#xff1a;常量不允许重新…

stable diffusion webui学习总结(3):参数设置

一、2.5D偏卡通风格参数设置&#xff1a; 步骤1、文生图 模型&#xff1a;darkSushiMixMix VAE&#xff1a;vae-ft-mse-840000-ema-pruned 正面提示词&#xff1a;(masterpiece, high quality, highres,illustration),blurry background,[(white background:1.2)::5],(see-…

数据库索引面试的相关问题

查看索引的执行计划 索引失效的情况 1、索引列上做了计算&#xff0c;函数&#xff0c;类型转换等操作。索引失效是因为查询过程需要扫描整个索引并回表。代价高于直接全表扫描。 Like匹配使用了前缀匹配符“%abc” 字符串不加引号导致类型转换。 原因&#xff1a; 常见索…

阿里云香港轻量应用服务器网络线路cn2?

阿里云香港轻量应用服务器是什么线路&#xff1f;不是cn2。 阿里云香港轻量服务器是cn2吗&#xff1f;香港轻量服务器不是cn2。阿腾云atengyun.com正好有一台阿里云轻量应用服务器&#xff0c;通过mtr traceroute测试了一下&#xff0c;最后一跳是202.97开头的ip&#xff0c;1…

酒店内部服务App开发常用的代码分享!

随着移动互联网的迅猛发展&#xff0c;智能手机已经成为人们生活中不可或缺的一部分&#xff0c;酒店作为服务行业的重要代表&#xff0c;为了提升客户体验、提高服务效率&#xff0c;纷纷开始开发内部服务App。 这些App不仅可以帮助酒店实现内部管理的便捷化&#xff0c;还能…

二叉树(4)——链式二叉树

1 二叉树的概念 二叉树是&#xff1a; 空树非空&#xff1a;根节点&#xff0c;根节点的左子树、根节点的右子树组成的。 二叉树定义是递归式的&#xff0c;因此后序基本操作中基本都是按照该概念实现的。 2 二叉树的遍历 2.1 前序、中序以及后序遍历 学习二叉树结构&#xf…

【C++笔记】第一阶段:C++基础入门

C基础入门 1 C初识1.1 创建一个C程序1.1.1 创建项目1.1.2 创建文件1.1.3 编写代码1.1.4 运行程序 1.2 注释1.3 变量1.4 常量1.5 关键字1.6 标识符命名规则 2 数据类型2.1 整型2.2 sizeof关键字2.3 实型&#xff08;浮点型&#xff09;2.4 字符型2.5 转义字符2.6 字符串型2.7 布…

ts总结大全

ts类型 TS类型除了原始js类型之外&#xff0c;还增加类型&#xff0c;例如&#xff1a;枚举、接口、泛型、字面量、自定义、类型断言、any、类型声明文件 数组类型两种写法&#xff1a;类型 [] 或 Array <类型> let arr:number[][1,2,3,4] let arr:string[][a] let arr…

全网最全软件测试面试八股文

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 一般软件测试的面试分为三轮&#xff1a;笔试&#xff0c;HR面试&#xff0c;技术面试。 前两轮…

【Go】四、rpc跨语言编程基础与rpc的调用基础原理

Go管理工具 早期 Go 语言不使用 go module 进行包管理&#xff0c;而是使用 go path 进行包管理&#xff0c;这种管理方式十分老旧&#xff0c;两者最显著的区别就是&#xff1a;Go Path 创建之后没有 go.mod 文件被创建出来&#xff0c;而 go module 模式会创建出一个 go.mod…

Github 2024-02-17 开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2024-02-17统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目4TypeScript项目3Rust项目2Jupyter Notebook项目1PowerShell项目1JavaScript项目1 Black&#xff…

标准IO 2月4日学习笔记

IO输入输出&#xff0c;操作对象是文件 Linux文件类型: b block 块设备文件 按块扫描设备信息的文件 存储设备 c character 字符设备文件 按字符扫描设备信息的文件 d direct…

单片机学习笔记---AD模数转换DA数模转换

目录 AD模数转换 XPT2046.c XPT2046.h main.c DA数模转换 main.c 上一篇博客讲了AD/DA转换的工作原理&#xff0c;也介绍了运算放大器的工作原理&#xff0c;这节开始代码演示&#xff01; AD模数转换 新创建一个工程&#xff1a;AD模数转换 第一个工程将用到LCD1602和…

相机图像质量研究(34)常见问题总结:图像处理对成像的影响--拖影

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

Linux | Ubuntu通过USB访问Redmi K40存储出现xxx was not providedby any .service files错误

问题描述 通过USB Type-C数据线将Redmi K40手机&#xff08;查证发现其他Redmi系手机也存在如此问题&#xff09;连接至台式机Ubuntu 20.04后&#xff0c;手机切换至访问存储模式&#xff0c;Ubuntu上可以访问手机存储&#xff0c;并正常进行文件传输。标题所述问题的诱发原因…