数据结构详细笔记——图

news2024/12/29 14:38:32

文章目录

  • 图的定义
  • 图的存储
    • 邻接矩阵法
    • 邻接表法
    • 邻接矩阵法与邻接表法的区别
  • 图的基本操作
  • 图的遍历
    • 广度优先遍历(BFS)
    • 深度优先遍历(DFS)
    • 图的遍历和图的连通性


图的定义

图G顶点集V边集E组成,记为G=(V,E),其中V(G)表示图G中顶点的有限非空集;E(G)表示图G中顶点之间的关系(边)集合,用|V|表示图G中顶点的个数,也称图G的阶,用|E|表示图G中边的条数

注意:线性表可以是空表,树可以是空树,但图不可以是空,即V一定是非空集

有向图
若E是有向边(也称)的有限集合时,则图G为有向图。弧是顶点的有序对,记为<v,w>,其中v、w是顶点,v称为弧尾,w称为弧头。<v,w> 不等于 <w,v>

无向图
若E是无向边(简称边)的有限集合时,则图G为无向图。边是顶点的有序对,记为(v,w)(v,w) 等于 (w,v)

简单图
①不存在重复的边
②不存在顶点到自身的边

多重图
图G中某两个结点之间的边数多于一条,又允许顶点通过同一条边和自己关联

顶点的度、入读、出度
对于无向图顶点v的度是指依附于该顶点的边的条数,记为TD(v)
对于有向图
入度是以顶点v为终点的有向边的数目,记为ID(v)
出度是以顶点v为起点的有向边的数目,记为OD(v)
顶点v的度等于其入度和出度之和,即TD(v)=ID(v)+OD(v)

顶点-顶点的关系

- 路径——顶点到顶点之间的一条路径

  • 回路——第一个顶点和最后一个顶点相同的路径称为回路或环
  • 简单路径——在路径序列中,顶点不重复出现的路径称为简单路径
  • 路径长度——路径上,边的数目
  • 点到点的距离——从顶点u出发到顶点v的最短路径若存在,则此路径的长度称为从u到v的距离;若从u到v根本不存在路径,则该距离为无穷(∞)
  • 无向图中,若从顶点v到顶点w有路径存在,则称v和w是连通的
  • 有向图中,若从顶点v到顶点w和从顶点w到顶点v之间都有路径,则称这两个顶点是强连通的

连通图、强连通图
若图G中 任意两个顶点都是连通的,则称图G为 连通图,否则称为非连通图

对于n个顶点的无向图G
若G是连通图,则最少有 n-1 条边
若G是非连通图,则最多可能有在这里插入图片描述条边

若图中 任何一对顶点都是强连通的,则称此图为 强连通图

对于n个顶点的有向图G
若G是强连通图,则最少有n条边(形成回路)

图的局部——子图
设有两个图G(V,E)和G’=(V’,E’),若V’是V的子集,且E’是E的子集,则称G‘是G的 子图

若有满足V(G’)=V(G)—— 顶点相同的子图G’,则称其为G的 生成子图

连通分量
无向图中的 极大连通子图(子图必须连通,且包含尽可能多的顶点和边)称为连通分量

强连通分量
有向图中的 极大强连通子图(子图必须连通,且包含尽可能多的顶点和边)称为强连通分量

生成树
连通图的生成树包含图中全部顶点的一个极小连通子图

若图中顶点数为n,则它的生成树含有n-1条边,对生成树而言,若砍去它的一条边,则会变成非连通图,若加上一条边则会形成一个回路

生成森林
在非连通图中,连通分量的生成树构成了非连通图的生成森林

边的权、带权图/网
边的权——在一个图中,每条边都可以标上具有某种含义的数值,该数值称为该边的权值
带权图/网——边上带有权值的图称为带权图,也称网
带权路径长度——当图是带权图时,一条路径上所有边的权值之和,称为该路径的带权路径长度

几种特殊形态的图
无向完全图——无向图中任意两个顶点之间存在边
有向完全图——有向图中任意两个顶点之间都存在方向相反的两条弧
树——不存在回路,且连通的无向图
有向树——一个顶点的入度为0,其余顶点的入度均为1的有向图

总结

对于n个顶点的无向图G
所有顶点的度之和等于2|E|
若G是连通图,则最少有n-1条边(树),若 |E|>n-1,则一定有回路
若G是非连通图,则最多可能有在这里插入图片描述条边
无向完全图共有在这里插入图片描述条边

对于n个顶点的有向图G
所有顶点的出度之和=入度之和= |E|
所有顶点的度之和等于 2|E|
若G是强连接图,则最少有n条边(形成回路)
有向完全图共有在这里插入图片描述条边

图的存储

邻接矩阵法

#define MaxVertexNum 100           // 顶点数目的最大值
typedef struct{
	char Vex[MaxVertexNum];         // 顶点表
	int Edge[MaxVertexNum][MaxVertexNum];  //邻接矩阵,边表
	int vexnum,arcnum;         // 图的当前顶点数和边数/弧数
}MGraph;

邻接矩阵存储带权图

#define MaxVertexNum 100              // 顶点数量的最大值
#define INFINITY                      // 宏定义常量“无穷”
typedef char VertexType;              // 顶点的数据类型
typedef int EdgeType;                 // 带权图中边上权值的数据类型
typedef struct{
	VertexType Vex[MaxVertexNum];     // 顶点
	EdgeType Edge[MaxVertexNum][MaxVertexNum];    // 边的权
	int vexnum,arcnum;                // 图的当前顶点数和弧数
}MGraph;

空间复杂度
O(|V|²) ——只和顶点数相关,和实际的边数无关
适合于存储稠密图
无向图的邻接矩阵是对称矩阵,可以压缩存储(只存储上三角区/下三角区)

邻接矩阵法的性质
设图G的邻接矩阵为A(矩阵元素为0/1),则Aⁿ的元素Aⁿ[ i ][ j ]等于由顶点 i 到顶点 j 的长度为 n 的路径的数目

举个🌰:
A:
| A | B | C | D |
| A | 0 | 1 | 0 | 0 |
| B | 1 | 0 | 1 | 1 |
| C | 0 | 1 | 0 | 1 |
| D | 0 | 1 | 1 | 0 |

A² [ 1 ] [ 4 ] = a(1,1)a(1,4) + a(1,2)a(2,4) + a(1,3)a(3,4) + a(1,4)a(4,4) = 1
说明 顶点A到顶点D的长度为2的路径有1条

邻接表法

邻接表法——顺序+链式存储

// 边/弧
typedef struct ArcNode{
	int adjvex;       // 边/弧指向哪个结点
	struct ArcNode *next;  // 指向下一条弧的指针
}ArcNode;

// 顶点
typedef struct VNode{
	VertexType data;    // 顶点信息
	ArcNode *first;     // 第一条边/弧
}VNode,AdjList[MaxVertexNum];

// 用邻接表存储的图
typedef struct{
	AdjList vertices;
	int vexnum,arcnum;
}ALGraph;

无向图——边结点的数量是2|E|,整体空间复杂度为 O(|V|+2|E|)
有向图——边结点的数量是|E|,整体空间复杂度为 O(|V|+|E|)

邻接矩阵法与邻接表法的区别

邻接表邻接矩阵
空间复杂度无向图:O(∣V∣+2∣E∣);有向图:O(∣V∣+∣E∣)O(∣V∣²)
适用于存储稀疏图存储稠密图
表示方式不唯一唯一
计算度/出度/入度计算有向图的度、入度不方便,其余很方便必须遍历对应行或列
找相邻的边找有向图的入边不方便,其余很方便必须遍历对应行或列

图的基本操作

  • Adjacent(G,x,y):判断图G是否存在边<x,y>或(x,y)
    邻接矩阵————O(1)
    邻接表 ————O(1)~O(|V|)

  • Neighbors(G,x):列出图G中与结点x邻接的边
    无向图:
    邻接矩阵————O(|V|)
    邻接表 ————O(1)~O(|V|)
    有向图:
    邻接矩阵————O(|V|)
    邻接表 ————出度:O(1)~O(|V|);入度:O(|E|)

  • InsertVertex(G,x):在图G中插入顶点x
    邻接矩阵————O(1)
    邻接表 ————O(1)

  • DeleteVertex(G,x):在图G中删除顶点x
    无向图:
    邻接矩阵————O(|V|)
    邻接表 ————O(1)~O(|V|)
    有向图:
    邻接矩阵————O(|V|)
    邻接表 ————删出边:O(1)~O(|V|);删入边:O(|E|)

  • AddEdge(G,x,y):若无向边(x,y)或有向边<x,y>不存在,则向图G中添加该边
    邻接矩阵————O(1)
    邻接表 ————O(1)

  • FirstNeighbor(G,x):求图G中顶点x的第一个邻接点,若有则返回顶点号,若没有邻接点或图中不存在x,则返回-1
    无向图:
    邻接矩阵————O(1)~O(|V|)
    邻接表 ————O(1)
    有向图:
    邻接矩阵————O(1)~O(|V|)
    邻接表 ————找出边:O(1);找入边:O(1)~O(|E|)

  • NextNeighbor(G,x,y):假设图G中顶点y是顶点x的一个邻接点,返回除y之外顶点x的下一个邻接点的顶点号,若y是x的最后一个邻接点,则返回-1
    邻接矩阵————O(1)~O(|V|)
    邻接表 ————O(1)

  • Get_edge_value(G,x,y):获取图G中边(x,y)或<x,y>对应的权值

  • Set_edge_value(G,x,y):设置图G中边(x,y)或<x,y>对应的权值

图的遍历

广度优先遍历(BFS)

图的广度优先遍历和树的层次遍历很相似
代码实现:

bool visited[MAX_VERTEX_NUM];   // 访问标记数组

// 广度优先遍历
void BFS(Graph G, int v){    // 从顶点v出发,广度优先遍历图G
	visit(v);                // 访问初始顶点v
	visited[v] = true;       // 对v做已访问标记
	EnQueue(Q,v);            // 顶点v入队列Q
	while(!isEmpty(Q)){
		DeQueue(Q,v);        // 顶点v出队列
		for(w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w)){
			// 检测v所有邻接点
			if(!visited[w]){    // w为v的未访问的邻接顶点
				visit(w);           // 访问顶点w
				visited[w]=true;    // 对w做已访问标记
				EnQueue(Q,w);       // 顶点w入队列
			}
		}
	}
}
	

如果是非连通图,则无法遍历完所有的结点,以上的代码就出现了Bug
所以先对所有的顶点遍历,还需要加上以下代码

void BfsTraverse(Graph G){
	for(i=0;i<G.vexnum;++i){
		visited[i]=false;  // 访问标记数组
	}
	InitQueue(Q);          // 初始化辅助队列Q
	for(i=0;i<G.vexnum;++i){   // 从0号顶点开始遍历
		if(!visited[i])    // 对每一个连通分量调用依次BFS
			BFS(G,i);      // vi未访问过,从vi开始BFS
	}
}

结论:对于无向图,调用BFS函数的次数=连通分量数

注意:
同一个图的邻接矩阵表示方式唯一,因此广度优先遍历序列唯一
同一个图的邻接表表示方式不唯一,因此广度优先遍历序列不唯一

复杂度分析
对于邻接矩阵存储的图:O(|v|²)
对于邻接表存储的图:O(|V|+|E|)

广度优先生成树
由广度优先遍历过程确定,将遍历过程中没有经过的边去掉

由于邻接表的表示方式不唯一,因此基于邻接表的广度优先生成树也不唯一

遍历非连通图可以得到广度优先生成森林

深度优先遍历(DFS)

图的深度优先遍历和树的先根遍历很相似

bool visited[MAX_VERTEX_NUM];   // 访问标记数组
void DFSTraverse(Graph G){
	for(v=0;v<G.vexnum;++v)
		visited[v]=false;
	for(v=0;v<G.vexnum;++v)
		if(!visited[v])
			DFS(G,v)
}


void DFS(Graph G,int v){
	visit(v);
	visited[v]=true;
	for(w=FirstNeighbor(G,v);w>=0;w=NextNeighbor(G,v,w)){
		if(!visited[w]){
			DFS(G,w);
		}
	}
}

结论:对于无向图,调用D FS函数的次数=连通分量数

复杂度分析
空间复杂度:O(|v|)
对于邻接矩阵存储的图:O(|v|²)
对于邻接表存储的图:O(|V|+|E|)

深度优先遍历序列

注意:
同一个图的邻接矩阵表示方式唯一,因此深度优先遍历序列唯一
同一个图的邻接表表示方式不唯一,因此深度优先遍历序列不唯一

深度优先生成树
由深度优先遍历过程确定,将遍历过程中没有经过的边去掉

由于邻接表的表示方式不唯一,因此基于邻接表的深度优先生成树也不唯一

遍历非连通图可以得到深度优先生成森林

图的遍历和图的连通性

无向图:
DFS/BFS函数调用次数=连通分量数
有向图:
1、若从起始顶点到其他顶点都有路径,则只需要调用1次DFS/BFS函数
2、对于强连通图,从任一顶点出发都只需要调用1次DFS/BFS函数

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

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

相关文章

uniapp-轮播图点击预览功能

实现效果 点击后打开预览图 实现代码 <swiper v-if"this.bannerList.length > 1" class"swiper" autoplay"true" duration"500" interval"2000" change"changeSwiper"><swiper-item class"swip…

vue下载xlsx表格

vue下载xlsx表格 // 导入依赖库 import XLSX from xlsx; import FileSaver from file-saver; methods:{btn(){let date new Date()let Y date.getFullYear() -let M (date.getMonth() 1 < 10 ? 0 (date.getMonth() 1) : date.getMonth() 1) -let D (date.getDat…

抖音小店没有流量?如何快速起店?实操经验分享!

我是电商珠珠 做抖音小店最重要的就是流量&#xff0c;店铺没有流量也就意味着销量上不去&#xff0c;最后只能被平台清退。 我做抖音小店也已经快三年的时间了&#xff0c;这种情况我也遇到过&#xff0c;接下来给大家讲讲解决方案。 第一步&#xff0c;选品 品是整个店铺…

电子商务税收问题:跨境电商的挑战与解决

随着电子商务的崛起&#xff0c;跨境电商已经成为全球贸易的主要动力之一。然而&#xff0c;电子商务的快速发展也带来了一系列税收问题&#xff0c;尤其是涉及跨境交易的税收问题。本文将深入探讨跨境电商所面临的税收挑战&#xff0c;以及政府和国际组织正在采取的解决方案。…

Matlab通信仿真系列——信号处理函数

微信公众号上线&#xff0c;搜索公众号小灰灰的FPGA,关注可获取相关源码&#xff0c;定期更新有关FPGA的项目以及开源项目源码&#xff0c;包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等 本节目录 一、Matlab信号产生函数…

MATLAB中std函数用法

目录 语法 说明 示例 矩阵列的标准差 三维数组的标准差 指定标准差权重 矩阵行的标准差 数组页的标准差 排除缺失值的标准差 标准差和均值 标准差 std函数的功能是得到标准差。 语法 S std(A) S std(A,w) S std(A,w,"all") S std(A,w,dim) S std(A…

【PTA题目】6-13 求叠数(递归版) 分数 10

6-13 求叠数(递归版) 分数 10 全屏浏览题目 切换布局 作者 李祥 单位 湖北经济学院 请编写递归函数&#xff0c;生成叠数。 例如&#xff1a;Redup(5,8)88888 函数原型 long long Redup(int n, int d); 说明&#xff1a;参数 n 为重复次数(非负整数)&#xff0c;d 为数字…

国鑫受邀出席2023英特尔中国区数据中心渠道客户金秋会

10月18日&#xff0c;2023英特尔中国区数据中心渠道客户金秋会在重庆隆重举行&#xff0c;本次会议共邀请了全国英特尔数据中心渠道生态伙伴、本地服务器系统设计及制造厂商参与&#xff0c;Gooxi作为英特尔在中国区重要合作伙伴受邀参加。 会上&#xff0c;英特尔向生态合作伙…

并行计算机系统结构基础

一、并行计算机系统结构 1.并行性 并行性&#xff1a;计算机系统在同一时刻或者同一时间间隔内 进行多种运算或操作 并行性包括两方面的含义 同时性&#xff1a;两个或两个以上的事件在同一时刻发生并发性&#xff1a;两个或两个以上的事件在同一时间间隔 内发生 从处理数…

新品首发 | HP1011:高性能双相交错 PFC 数字控制器

随着PFC技术的发展&#xff0c;不断有新型PFC拓扑结构提出&#xff0c;如单相PFC、交错并联 PFC、传统无桥PFC、图腾柱无桥 PFC等。交错Boost PFC系统不仅具有并联系统的所有优点&#xff0c;还能减少输入电流纹波&#xff0c;降低开关管的电流应力。在中大功率场所通常采用工作…

vue + antd 动态增加表单并进行表单校验

<template><a-modalv-model:visible="visible":title="formData.id ? 编辑渠道 : 添加渠道":width="850":mask-closable="false":destroy-on-close="true"@ok="onSubmit"@cancel="onClose"&g…

【算法萌新闯力扣】:两个数组的交集

力扣热题&#xff1a;两个数组的交集 开篇 今天早上状态不错&#xff0c;花了较短的时间刷了4道力扣算法题。挑选了一道还不错的题目与大伙分享。 题目链接:349.两个数组的交集 题目描述 代码思路 看到题目后&#xff0c;想到可以把一个数组用集合存起来&#xff0c;然后用…

C语言真的需要头文件吗?

C语言真的需要头文件吗&#xff1f; 头文件的作用是什么&#xff1f; 如果你直接定义了函数&#xff0c;当然不需要头文件。 因为调用函数&#xff0c;你得知道函数的参数有多少&#xff0c;都什么类型的&#xff0c;返回值是什么&#xff0c;这样才能调用。最近很多小伙伴找…

超详细的Jmeter接口测试教程以及接口测试流程

一、Jmeter简介 Jmeter是由Apache公司开发的一个纯Java的开源项目&#xff0c;即可以用于做接口测试也可以用于做性能测试。 Jmeter具备高移植性&#xff0c;可以实现跨平台运行。 Jmeter可以实现分布式负载。 Jmeter采用多线程&#xff0c;允许通过多个线程并发取样或通过…

HLS基础issue

hls 是一个用C/c 来开发PL &#xff0c;产生rtl的工具 hls是按照rtl code来运行的 &#xff0c; 但是rtl会在不同器件调用不同的源语&#xff1b; 可能产生的ip使用在vivado另外一个器件的话 会存在问题&#xff1b; Hls &#xff1a; vivado ip &#xff0c; vitis kernel 是…

【IDEA 使用easyAPI、easyYapi、Apifox helper等插件时,导出接口文档缺少代码字段注释的相关内容、校验规则的解决方法】

问题 IDEA 使用easyAPI、easyYapi、Apifox helper等插件时&#xff0c;导出的接口文档上面&#xff0c;缺少我们代码里的注解字段&#xff0c;如我们规定了NOTNULL、字段描述等。 问题链接&#xff0c;几个月之前碰到过&#xff0c;并提问了&#xff0c;到现在解决&#xff0c…

用华为Mate60系列体验玩机功能,乐趣翻倍!

AI隔空操控&#xff0c;隔空滑动屏幕&#xff0c;随心操作更高效&#xff1b; AI信息保护&#xff0c;智能识别人脸&#xff0c;智慧消息隐藏&#xff1b; 智感支付&#xff0c;快人一步&#xff0c;一步即付更迅捷~

助力水泥基建裂痕自动化巡检,基于yolov5融合ASPP开发构建多尺度融合目标检测识别系统

道路场景下的自动化智能巡检、洞体场景下的壁体类建筑缺陷自动检测识别等等已经在现实生活中不断地落地应用了&#xff0c;在我们之前的很多博文中也已经有过很多相关的实践项目经历了&#xff0c;本文的核心目的是想要融合多尺度感受野技术到yolov5模型中以期在较低参数量的情…

人工智能飞速发展的今天,IT行业能做些什么?

原创 | 文 BFT机器人 01 IT行业&#xff1a;信息流通的“媒介” IT行业作为一个信息化产业&#xff0c;通过运用信息手段和技术&#xff0c;为客户收集、整理、储存、传递信息情报&#xff0c;提供信息服务&#xff0c;并提供相应的信息手段、信息技术等服务。 近年来&#xf…

C生万物 | 从浅入深理解指针【最后部分】

C生万物 | 从浅入深理解指针【最后部分】 文章目录 C生万物 | 从浅入深理解指针【最后部分】前言sizeof和strlen的对比sizeofstrlen 数组和指针笔试题解析一维数组字符数组二维数组 前言 我们前面学了四个部分了&#xff0c;如果没有看前面的建议可以看一下前面的~~ C生万物 |…