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

六、图

目录

  • 六、图
    • 定义及基本术语
      • 图的定义
      • 有向图以及无向图
      • 简单图以及多重图
      • 顶点-顶点间关系
      • 连通图、强连通图
      • 子图
      • 连通分量
      • 强连通分量
      • 生成树
      • 生成森林
      • 边的权、带权网/图
      • 特殊形态的图
    • 图的存储及基本操作
      • 邻接矩阵
      • 邻接表法
      • 十字链表
      • 邻接多重表
      • 分析对比
      • 图的基本操作
    • 图的遍历
      • 广度优先遍历(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数据结构笔记

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

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

相关文章

pytorch实现线性回归

转大佬笔记 代码&#xff1a; # -*- coding: utf-8 -*- # Time : 2023-07-14 14:57 # Author : yuer # FileName: exercise05.py # Software: PyCharm import matplotlib.pyplot as plt import torch# x,y是3行1列的矩阵&#xff0c;所以在[]中要分为3个[] x_data torch.…

系统学习Linux-SSH远程服务(二)

概念 安全外壳协议&#xff0c;提供安全可靠的远程连接 特点 ssh是工作在传输层和应用层的协议 ssh提供了一组管理命令 ssh 远程登陆 scp 远程拷贝 sftp 远程上传下载 ssh-copy-id ssh keygen 生成 提供了多种身份验证机制 身份验证机制 密码验证 需要提供密码 密…

Django实现接口自动化平台(十二)自定义函数模块DebugTalks 序列化器及视图【持续更新中】

上一章&#xff1a; Django实现接口自动化平台&#xff08;十一&#xff09;项目模块Projects序列化器及视图【持续更新中】_做测试的喵酱的博客-CSDN博客 本章是项目的一个分解&#xff0c;查看本章内容时&#xff0c;要结合整体项目代码来看&#xff1a; python django vue…

Python SMTP发送邮件

如何使用Python发送QQ邮件&#xff1f;如何发送带附件的邮件&#xff1f;这篇文章将详细说明 目录 一、发送邮件 二、发送HTML格式的邮件 三、在HTML中添加图片 四、发送带附件的邮件 五、最终整合版 六、配置指引 一、发送邮件 import smtplib from email.mime.text im…

【UE4 塔防游戏系列】09-防御塔升级、击杀敌人增加金钱

目录 效果 步骤 一、控件蓝图文本控件内容绑定金钱数 二、防御塔改造 三、击杀敌人增加金钱 四、防御塔升级功能 效果 步骤 一、控件蓝图文本控件内容绑定金钱数 1. 打开“TaFangGameMode”&#xff0c;新增一个变量命名为“PlayerMoney”&#xff0c;默认值设为2…

【Maven三】——maven生命周期和插件

系列文章目录 Maven之POM介绍 maven命令上传jar包到nexus 【Maven二】——maven仓库 maven生命周期和插件 系列文章目录前言一、什么是生命周期&why1.三套生命周期2.clean生命周期3.default生命周期4.site生命周期5.命令行与生命周期 二、插件目标三、插件绑定1.内置绑定2…

软通动力与华秋达成生态共创合作,共同推动物联网硬件创新

7月11日&#xff0c;在2023慕尼黑上海电子展现场&#xff0c;软通动力信息技术(集团)股份有限公司(以下简称“软通动力”)与深圳华秋电子有限公司(以下简称“华秋”)签署了生态共创战略合作协议&#xff0c;共同推动物联网硬件生态繁荣发展。当前双方主要基于软通动力的产品及解…

GO语言GMP模型

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

基于Selenium+Python的web自动化测试框架

一、什么是Selenium&#xff1f; Selenium是一个基于浏览器的自动化测试工具&#xff0c;它提供了一种跨平台、跨浏览器的端到端的web自动化解决方案。Selenium主要包括三部分&#xff1a;Selenium IDE、Selenium WebDriver 和Selenium Grid。 Selenium IDE&#xff1a;Firef…

Linux下Nginx升级

nginx版本升级不会覆盖配置文件&#xff0c;但以防万一升级前请先备份配置文件或者配置文件夹 默认配置文件地址&#xff1a;/usr/local/nginx/conf/nginx.conf 1.下载 wget -c http://nginx.org/download/nginx-1.24.0.tar.gz 2.解压 tar -xvf nginx-1.24.0.tar.gz 3.nginx…

Mac的docker安装redis

Mac的docker安装redis 1、docker search redis NAME DESCRIPTION STARS OFFICIAL AUTOMATED redis Redis is an open source key-value store that… 12205 …

git如何撤销commit(未push)

文章目录 前言undo commitreset current branch to here Undo Commit&#xff0c;Revert Commit&#xff0c;Drop Commit的区别 是否删除对代码的修改是否删除Commit记录是否会新增Commit记录Undo Commit不会未Push会&#xff0c;已Push不会不会Revert Commit会不会会Drop Com…

PHP与Golang对战:两种语言的比较与应用场景探讨

引言 在软件开发领域&#xff0c;选择一种合适的编程语言对于项目的成功至关重要。而在今天的文中&#xff0c;我们将探讨两个备受争议的编程语言——PHP与Golang之间的对战。通过比较它们的优势和应用场景&#xff0c;帮助开发者更好地了解如何选择适合自己项目的语言。 PHP的…

青翼科技自主研发4路AD子卡FMC137

FMC137是一款基于VITA57.4标准规范的JESD204B接口FMC子卡模块&#xff0c;该模块可以实现4路14-bit、2GSPS/2.6GSPS/3GSPS ADC采集功能。该板卡ADC器件采用ADI公司的AD9208芯片&#xff0c;&#xff0c;与ADI公司的AD9689可以实现PIN脚兼容。该ADC与FPGA的主机接口通过16通道的…

verilog实现数码管静态显示

文章目录 verilog实现数码管静态显示一、任务要求二、实验代码三、仿真代码四、仿真结果五、总结 verilog实现数码管静态显示 一、任务要求 六个数码管同时间隔0.5s显示0-f。要求&#xff1a;使用一个顶层模块&#xff0c;调用计时器模块和数码管静态显示模块。 二、实验代码…

分布式数据库HBase,它到底是怎么组成的?

原文链接&#xff1a;http://www.ibearzmblog.com/#/technology/info?id8ac4902f82f525e1456624d5d7a545dc 前言 大数据的核心问题无非就是存储和计算这两个。Hadoop中的HDFS解决了数据存储的问题&#xff0c;而HBase就是在HDFS上构建&#xff0c;因此Hbase既能解决大数据存…

echarts实现渐变折线图并添加点击事件

折线图点击事件代码: let myChart = this.$echarts.init(document.getElementById(trendBoxECharts))myChart.getZr().on(click, params => {console.log(params)let pointInPixel = [params.offsetX, params.offsetY]if (myChart.containPixel(grid, pointInPixel)) {//点…

基于FME二开产品:NewGIS integration介绍

目录 前言 一、模板上传 二、模板在线运行 1.模板参数解析 2.模板运行 三、成果管理 总结 前言 爆肝两个月&#xff0c;我和我的团队终于打造出了一款能完美适配所有FME模板的在线模板管理平台&#xff0c;目前支持FME2021版本的所有模板的在线运行、管理。整体技术框架…

hibernate入门,springboot整合hibernate

Mybatis和Hibernate是我们常用的两大ORM框架&#xff0c;这篇文章主要介绍hibernate的使用&#xff0c;如何通过springboot整合hibernate&#xff0c;实现简单的crud功能。 添加依赖 首先&#xff0c;需要创建一个springboot项目&#xff0c;这里就取名为hibernate。项目创建完…

Stable Diffusion 丝滑无闪烁AI动画 Temporalkit+Ebsynth+Controlnet

早期的EbSynth制作的AI视频闪烁能闪瞎人的双眼,可以通过【temporalkit+ebsynth+controlnet】让视频变得丝滑不闪烁。 文章目录 插件准备丝滑视频制作插件准备 下载安装 EbSynth官网,这里需要输入email地址。 下载压缩包解压缩到任意位置,这里我放到了ebsynth_utility下。 …