【数据结构】24王道考研笔记——图

news2024/11/23 11:03:26

六、图

目录

  • 六、图
    • 定义及基本术语
      • 图的定义
      • 有向图以及无向图
      • 简单图以及多重图
      • 顶点-顶点间关系
      • 连通图、强连通图
      • 子图
      • 连通分量
      • 强连通分量
      • 生成树
      • 生成森林
      • 边的权、带权网/图
      • 特殊形态的图
    • 图的存储及基本操作
      • 邻接矩阵
      • 邻接表法
      • 十字链表
      • 邻接多重表
      • 分析对比
      • 图的基本操作
    • 图的遍历
      • 广度优先遍历(BFS)
      • 深度优先遍历(DFS)
    • 图的应用
      • 最小生成树
      • 最短路径BFS
      • 最短路径Dijkstra
      • 最短路径Floyd算法
      • 有向无环图
      • 拓扑排序
      • 关键路径

定义及基本术语

图的定义

image.png

有向图以及无向图

image.png

简单图以及多重图

image.png

image.png

顶点-顶点间关系

image.png

连通图、强连通图

image.png

子图

image.png

(有向图也一样)

连通分量

image.png

强连通分量

image.png

生成树

image.png

生成森林

image.png

边的权、带权网/图

image.png

特殊形态的图

image.png

image.png

image.png

总结:

image.png

图的存储及基本操作

邻接矩阵

#define MaxVertexNum 100//顶点数目的最大值
typedef struct
{
	char Vex[MaxVertexNum];//顶点表 (可存更复杂的信息)
	int Edge[MaxVertexNum][MaxVertexNum];//邻接矩阵,边表(可以用bool型或枚举型变量表示边)
	int vexnum, arcnum;//图的当前顶点数和边数/弧数
}MGraph;

image.png

image.png

存储带权图(网):

image.png

对角线处可以填0或∞

空间复杂度为O(|V|2)只和顶点数相关,和实际的边数无关,适合用于存储稠密图

对于无向图,邻接矩阵是对称矩阵,可以进行对称矩阵的存储压缩,存入一维数组中(只存储上三角区/下三角区)

性质:

image.png

邻接表法

邻接矩阵是用数组实现的顺序存储,空间复杂度高,不适合存储稀疏图

而邻接表法使用顺序+链式存储的方式,表示方式并不唯一(与树的孩子表示法相似)

//邻接表
typedef char VertexType;//顶点的数据类型
//“边/弧”
typedef struct ArcNode
{
	int adjvex;//边/弧指向哪个节点
	struct ArcNode* next;//指向下一条弧的指针
	//InfoType info;//权值
}ArcNode;
//“顶点”
typedef struct VNode
{
	VertexType data;//顶点信息
	ArcNode* first;//第一条边/弧
}VNode,AdjList[MaxVertexNum];
//用邻接表存储的图
typedef struct
{
	AdjList vertices;
	int vexnum, arcnum;
}ALGraph;

image.png

与邻接矩阵对比:

image.png

十字链表

用邻接矩阵以及邻接表存储有向图时,都有所缺陷:

image.png

使用十字链表存储有向图(不能用于无向图):

image.png

空间复杂度为:O(|V|+|E|)

顺着绿色路线能找到顶点所有的出边

顺着橙色路线能找到顶点所有的入边

邻接多重表

用邻接矩阵以及邻接表存储无向图时,都有所缺陷:

image.png

用邻接多重表存储无向图(不能用于有向图):

image.png

空间复杂度:O(|V|+|E|)

删除边、删除节点等操作很方便

分析对比

image.png

图的基本操作

主要基于图的邻接矩阵以及邻接表

image.png

**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(|E|)

**InsertVertex(G,x):**在图G中插入顶点x

邻接矩阵:O(1) 邻接表:O(1)

**DeleteVertex(G,x):**从图G中删除节点x

邻接矩阵:O(|V|) 邻接表:出边:O(1)~O(|V|) 入边:O(|E|)

**AddEdge(G,x,y):**若无向边(x, y)或有向边<x, y>不存在,则向图G中添加该边。

邻接矩阵:O(1) 邻接表:O(1)

**RemoveEdge(G,x,y):**若无向边(x, y)或有向边<x, y>存在,则从图G中删除该边。

邻接矩阵:O(1) 邻接表:O(1)~O(|V|)

**FirstNeighbor(G,x):**求图G中顶点x的第一个邻接点,若有则返回顶点号。若x没有邻接点或图中不存在x,则返回-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)

图的遍历

广度优先遍历(BFS)

实现思路:

image.png

#define MAX_VERTEX_NUM 100//顶点数目的最大值

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

//广度优先遍历
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入队列
			}
		}
	}
}

遍历序列是具有可变性的

image.png

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

复杂度分析:

image.png

image.png

广度优先生成树(若是非连通图,则得到广度优先生成森林)

image.png

利用广度优先生成的树,高度是最小的(因为按最短路径)

应用:解决非带权图的单源最短路径问题

//解决非带权图的单源最短路径问题
void BFS_MIN_Distance(Graph G, int u)
{
	//d[i]表示从u到i结点的最短路径
	for (int i = 0; i < G.vexnum; ++i)
	{
		d[i] = 0x3f3f3f3f;//无穷大,初始化路径长度
	}
	visited[u] = true;
	d[u] = 0;
	EnQueue(Q, u);
	while (!isEmpty(Q))//BFS算法主过程
	{
		DeQueue(Q, u);//队头元素u出队
		for (w = FirstNeighbor(G, u); w >= 0; w = NextNeighbor(G, u, w))
		{
			if (!visited[w])//w为u的尚未访问的邻接顶点
			{
				visited[w] = true;//设已访问标记
				d[w] = d[u] + 1;//路径长度加1
				EnQueue(Q, w);//顶点w入队
			}
		}
	}
}

深度优先遍历(DFS)

类似于树的先根遍历,使用函数调用栈,递归实现

#define MAX_VERTEX_NUM 100//顶点数目的最大值

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

void DFSTraverse(Graph G)//深度优先遍历图G
{
	for (v = 0; v < G.vexnum; ++v)
		visited[v] = false;//初始化已访问标记数据
	for (v = 0; v < G.vexnum; ++v)//从v=0开始遍历
		if (!visited[v])
			DFS(G, v);
}

void DFS(Graph G, int v)//从顶点v触发,深度优先遍历图G
{
	visit(v);//访问顶点v
	visited[v] = true;//设已访问标记
	for (w = FirstNeighbor(G, v); w >= 0; w = NextNeighbor(G, v, w))
	{
		if (!visited[w])
		{
			DFS(G, w); // w为v的尚未访问的邻接顶点
		}
	}
}

复杂度分析:

image.png

image.png

遍历序列不唯一性:

image.png

深度优先生成树:(非连通生成森林)

image.png

对于无向图进行BFS/DFS遍历,调用BFS/DFS次数=连通分量数,对于连通图,只调用一次

对于有向图进行BFS/DFS遍历,调用BFS/DFS次数要具体问题具体分析,若起始顶点到其他各顶点都有路径,只调用一次,对于强连通图,从任一结点出发都只需调用一次BFS/DFS

图的应用

最小生成树

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

广度优先生成树的高度是小于等于深度优先生成树的高度的。

最小生成树定义:

image.png

两种求最小生成树的方法:

Kruskal:

image.png

Prim:

image.png

image.png

最短路径BFS

最短路径问题描述:

image.png

利用BFS算法可以求无权图的单源最短路径(无权图可以视作一种特殊的带权图,只是每条边的权值都为1)故各边权值相等时,可以使用BFS算法求解

代码实现:

//解决非带权图的单源最短路径问题
void BFS_MIN_Distance(Graph G, int u)
{
	//d[i]表示从u到i结点的最短路径
	for (int i = 0; i < G.vexnum; ++i)
	{
		d[i] = 0x3f3f3f3f;//无穷大,初始化路径长度
        path[i]=-1;//记录最短路径从哪个顶点过来
	}
	visited[u] = true;
	d[u] = 0;
	EnQueue(Q, u);
	while (!isEmpty(Q))//BFS算法主过程
	{
		DeQueue(Q, u);//队头元素u出队
		for (w = FirstNeighbor(G, u); w >= 0; w = NextNeighbor(G, u, w))
		{
			if (!visited[w])//w为u的尚未访问的邻接顶点
			{
				visited[w] = true;//设已访问标记
				d[w] = d[u] + 1;//路径长度加1
                path[w]=u;//最短路径应从u到w
				EnQueue(Q, w);//顶点w入队
			}
		}
	}
}

image.png

时间复杂度:邻接矩阵O(|V|2) 邻接表O(|V|+|E|)

最短路径Dijkstra

dist[ ]记录从源点v0到其他各顶点当前的最短路径长度,它的初态为:若从v0到vi有弧,则dist[i]为弧上的权值,否则置于∞

path[ ]表示从源点到顶点i之间的最短路径的前驱结点。在算法结束时,可根据其值追溯得到源点v0到顶点vi的最短路径。

image.png

不适用于有负权值的带权图

用邻接矩阵以及邻接表时间复杂度都为O(|V|2)

最短路径Floyd算法

算法思路:

image.png

image.png

最终实现:

image.png

对于更多结点,若要找到完整路径需要通过path矩阵递归寻找

image.png

Floyd算法可以用于负权图,但不能解决带有“负权回路”的图(有负权值的边组成回路)这种图有可能没有最短路径

不同算法对比:

image.png

有向无环图

若一个有向图中不存在环,则称为有向无环图,简称DAG图

有向无环图是描述含有公共子式的表达式的有效工具

其表示表达式中顶点中不可能出现重复的操作数

步骤:

image.png

表示出来的结果可能不唯一

拓扑排序

AOV网:用DAG图表示一个工程,顶点表示活动,有向边<Vi,Vj>表示活动Vi必须先于活动Vj进行,不能存在环路!

拓扑排序:

image.png

image.png

实现代码:

#define MaxVertexNum 100//图中顶点数目的最大值

//定义邻接表
typedef char VertexType;//顶点的数据类型
//“边/弧”
typedef struct ArcNode
{
	int adjvex;//边/弧指向哪个节点
	struct ArcNode* nextarc;//指向下一条弧的指针
	//InfoType info;//权值
}ArcNode;
//“顶点”
typedef struct VNode
{
	VertexType data;//顶点信息
	ArcNode* firstarc;//第一条边/弧
}VNode, AdjList[MaxVertexNum];
//用邻接表存储的图
typedef struct
{
	AdjList vertices;
	int vexnum, arcnum;
}ALGraph;


//拓扑排序
bool TopologicalSort(Graph G)
{
	InitStack(S);//初始化栈,存储入度为0的结点
	for (int i = 0; i < G.vexnum; i++)
	{
		if (indegree[i] == 0)
		{
			Push(S, i);//将所有入度为0的顶点进栈
		}
	}
	int count = 0;//计数,记录当前已经输出的顶点数
	while (!IsEmpty(S))//栈不空,则存在入度为0的顶点
	{
		Pop(S, i);//栈顶元素出栈
		print(count++) = i;//输出顶点i
		for (p = G.vertices[i].firstarc; p; p = p->nextarc)
		{
			//将所有i指向的顶点的入度减1,并且将入度为0的顶点压入栈S
			v = p->adjvex;
			if (!(--indegree[v]))//若为0
			{
				Push(S, v);//入栈
			}
		}
	}
	if (count < G.vexnum)
	{
		return false;//排序失败,有向图中有回路
	}
	else
		return true;//拓扑排序成功
}

image.png

时间复杂度:邻接表:O(|V|+|E|) 若采用邻接矩阵 O(|V|2)

逆拓扑排序:

image.png

也可以用DFS算法实现:

//逆拓扑排序
void DFSTraverse(Graph G)//深度优先遍历图G
{
	for (v = 0; v < G.vexnum; ++v)
		visited[v] = false;//初始化已访问标记数据
	for (v = 0; v < G.vexnum; ++v)//从v=0开始遍历
		if (!visited[v])
			DFS(G, v);
}

void DFS(Graph G, int v)//从顶点v触发,深度优先遍历图G
{
	visit(v);//访问顶点v
	visited[v] = true;//设已访问标记
	for (w = FirstNeighbor(G, v); w >= 0; w = NextNeighbor(G, v, w))
	{
		if (!visited[w])
		{
			DFS(G, w); // w为v的尚未访问的邻接顶点
		}
	}
	print(v);//输出顶点
}

关键路径

在带权有向图中,以顶点表示事件,以有向边表示活动,以边上的权值表示完成该活动的开销(如完成活动所需的时间),称之为用边表示活动的网络,简称AOE网。

性质:

image.png

AOE网中仅有一个入度为0的顶点,称为开始顶点(源点),也仅有一个出度为0的顶点,称为结束顶点(汇点)

从源点到汇点的有向路径可能有多条,所有路径中,具有最大路径长度的路径称为关键路径,而把关键路径上的活动称为关键活动

完成整个工作的最短时间就是关键路径的长度,若关键活动 不能按时安成为,则整个工程的完成时间就会延长。

几个概念:

image.png

image.png

image.png

求关键路径的步骤:

image.png

求事件的最早发生时间:

image.png

求事件的最迟发生时间:

image.png

求活动的最早发生时间:

image.png

求活动的最迟发生时间:

image.png

求活动的时间余量:

image.png

最终得出关键路径:

image.png

特性:

若关键活动耗时增加,则整个工程的工期将增长,缩短关键活动的时间,可以缩短整个工程的工期,当缩短到一定程度时,关键活动可能会变成非关键活动。

可能有多条关键路径,只提高一条关键路径上的关键活动速度并不能缩短整个工程的工期,只有加快那些包括在所有关键路径上的关键活动才能达到缩短工期的目的。

思路总结:

image.png

主要参考:王道考研课程
后续会持续更新考研408部分的学习笔记,欢迎关注。
github仓库(含所有相关源码):408数据结构笔记

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

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

相关文章

# Linux下替换文件中的颜色等控制字符的方法

Linux下替换文件中的颜色等控制字符的方法 文章目录 Linux下替换文件中的颜色等控制字符的方法1 Linux下的控制字符&#xff08;显示的文字并不是他本身&#xff09;&#xff1a;2 颜色字符范例&#xff1a;3 替换4 最后 我们在shell编程显示输出时&#xff0c;会定义文字颜色&…

ESD放电模式以及电源箝位 (power clamp )电路

目录 1.ESD的基本概念 2.ESD放电模式与泄放路径 2.1 I/O端与 Vcc或者 I/O端与 Vss 2.2 I/O端与 I/O端 2.3 Vcc&#xff08;电源端&#xff09;与Vss&#xff08;地端&#xff09; 2.4不同类型电压源 3. 电源箝位 (power clamp )电路 4.全芯片ESD保护电路系统框图 参考…

wsl2 kali linux install android studio Android SDK platforms

studio下载网址为 https://developer.android.google.cn/ 解压后在bin目录下运行studio.sh提示错误&#xff1a; unable to access android sdk add-on list 选择“setup proxy” 选择Manual proxy configuration 设置 Host name 为&#xff1a;mirrors.neusoft.edu.cn 设置…

从零开发短视频电商 单元测试(TestNG)

文章目录 简介简单示例执行测试并查看测试报告方式一 在IDEA中运行testng.xml文件方式二 在IDEA中运行测试类或者package方式三 在Maven中运行测试 统计测试覆盖率方式一 IDEA 支持详细的代码测试覆盖率统计方式二 Maven支持测试覆盖率 在IDEA中创建测试用例使用 IDEA 快速创建…

LLaMA微调记录

本文基于开源代码https://github.com/Lightning-AI/lit-llama/tree/main执行微调 其他参考链接&#xff1a; Accelerating LLaMA with Fabric: A Comprehensive Guide to Training and Fine-Tuning LLaMA - Lightning AI 结构化数据示例&#xff1a; BelleGroup/train_0.5M_…

动态sql语句

1.1 动态sql语句概述 Mybatis 的映射文件中&#xff0c;业务逻辑复杂时&#xff0c; SQL是动态变化的&#xff0c;此时在前面的学习中 SQL 就不能满足要求了。 参考的官方文档&#xff1a; 1.2 动态 SQL 之<if> 根据实体类的不同取值&#xff0c;使用不同的 SQL语句…

常见安装工具以及命令(工作常用)长期维护

dockermongodbnginxredis 1.docker 启动2.docker 安装 MongoDB3.启动nginx4.redis配置&#xff0b;安装4.1 Redis的启动和停止4.2 后台启动方式 systemctl start docker redis-server /root/myredis/redis.conf docker start mymongo docker exec -it mymongo /bin/bash 1.doc…

代码随想录算法训练营第58天 | 单调栈 ●739 每日温度 ●496下一个更大元素I ●503下一个更大元素II ●42 接雨水 ●84 柱形图中最大的矩形

#单调栈&#xff1a; 单调栈就是保持栈内元素有序。和栈与队列&#xff08;239. 滑动窗口最大值 自己写一个class来实现单调队列&#xff09;一样&#xff0c;需要我们自己维持顺序&#xff0c;没有现成的容器可以用。 通常是一维数组&#xff0c;要寻找任一个元素的右边或者…

浅谈物联网工程专业:技术融合与未来发展

技术融合与未来发展 引言1. 专业的定义与概述2. 专业的知识体系3. 专业的实践应用4. 专业的发展趋势5. 专业的就业前景结语&#x1f340;小结&#x1f340; &#x1f389;博客主页&#xff1a;小智_x0___0x_ &#x1f389;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收…

zabbix 企业级级监控(1) 监控自己

重点一 Zabbix简介在企业网络运维过程中&#xff0c;管理员必须随时关注各服务器和网络的运行状况&#xff0c;以便及时发现问题&#xff0c;尽可能减少故障的发生。当网络中的设备&#xff0c;服务器等数量较多时&#xff0c;为了更加方便&#xff0c;快捷的获得监控信息&…

【软件测试面试】腾讯数据平台笔试题-接口-自动化-数据库

数据库题 答案&#xff1a; Python编程题 答案&#xff1a; 接口参数化题 答案&#xff1a; 接口自动化题 答案&#xff1a; 以下是我收集到的比较好的学习教程资源&#xff0c;虽然不是什么很值钱的东西&#xff0c;如果你刚好需要&#xff0c;可以评论区&#…

6.3.6 利用Wireshark进行协议分析(六)----网页提取过程的协议分析

6.3.6 利用Wireshark进行协议分析&#xff08;六&#xff09;----网页提取过程的协议分析 利用Wireshark捕获网页访问过程中产生的应用协议报文&#xff0c;还原Web服务中报文的交互过程&#xff0c;为了防止网页直接从本地缓存中获取&#xff0c;我们首先需要清空浏览器保存的…

GO语言GMP模型

目录 程序入口 协程主动让出: 被动让出: schedule 监控线程 程序入口 在执行一系列检查和初始化&#xff08;创建多少个P&#xff0c;与M&#xff10;关联&#xff09;后&#xff0c;进入runtime.main,创建main goroutine,执行mian.mian。 一开始GO语言的调度只有M和G。每个M…

【代码随想录 | Leetcode | 第七天】链表 | 链表相交 | 环形链表 II

前言 欢迎来到小K的Leetcode|代码随想录|专题化专栏&#xff0c;今天将为大家带来链表相交和环形链表 II的分享✨ 目录 前言面试题 02.07. 链表相交142. 环形链表 II总结 面试题 02.07. 链表相交 ✨题目链接点这里 给你两个单链表的头节点 headA 和 headB &#xff0c;请你找…

C/C++ new A与new A()的区别

在C中&#xff0c;POD是“Plain Old Data”的缩写&#xff0c;即“普通旧数据”。POD data是指一种特殊类型的数据结构&#xff0c;它们具有简单的内存布局&#xff0c;没有构造函数、虚函数、私有/保护非静态数据成员&#xff0c;也没有虚继承等特性。这些数据结构可以直接通过…

k8s与集群管理

从docker讲起 终于有人把 Docker 讲清楚了&#xff0c;万字详解&#xff01; Docker资源&#xff08;CPU/内存/磁盘IO/GPU&#xff09;限制与分配指南 默认情况下&#xff0c;Docker容器是没有资源限制的&#xff0c;它会尽可能地使用宿主机能够分配给它的资源。如果不对容器资…

C++--day3(内联函数、结构体、类、封装、this、构造函数、析构函数)

#include <iostream>using namespace std;class My_stack { private:int *ptr; //指向堆区空间int top; //记录栈顶元素int size; public://有参构造My_stack(int size):ptr(new int[size]),top(-1){this->sizesize;cout<<"My_stack::有参构造&…

基于STM32的智能喂养系统

基于STM32的智能喂养系统 系统简介 自动检测环境温湿度&#xff0c;当温湿度低于阈值时自动打开加湿器&#xff1b;自动检测水位&#xff0c;当水位低于阈值时自动加水&#xff1b;自动检测有害气体&#xff0c;当检测到有害气体时自动打开风扇&#xff1b;同步状态到微信小程…

中间件上云部署 zookeeper

中间件上云部署 zookeeper 企业级中间件上云部署 zookeeper一、环境说明二、zookeeper部署YAML资源清单准备三、zookeeper部署及部署验证四、zookeeper应用验证 企业级中间件上云部署 zookeeper 一、环境说明 storageclassingress 二、zookeeper部署YAML资源清单准备 # vim…

图解java.util.concurrent并发包源码系列,原子类、CAS、AtomicLong、AtomicStampedReference一套带走

图解java.util.concurrent并发包源码系列&#xff0c;原子类、CAS、AtomicLong、AtomicStampedReference一套带走 原子类为什么要使用原子类CAS AtomicLong源码解析AtomicLong的问题ABA问题AtomicStampedReference 高并发情况下大量的CAS失败&#xff0c;导致CPU空转 往期文章&…