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

有向图以及无向图

简单图以及多重图

度

顶点-顶点间关系

连通图、强连通图

子图

(有向图也一样)
连通分量

强连通分量

生成树

生成森林

边的权、带权网/图

特殊形态的图



总结:

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


存储带权图(网):

对角线处可以填0或∞
空间复杂度为O(|V|2)只和顶点数相关,和实际的边数无关,适合用于存储稠密图
对于无向图,邻接矩阵是对称矩阵,可以进行对称矩阵的存储压缩,存入一维数组中(只存储上三角区/下三角区)
性质:

邻接表法
邻接矩阵是用数组实现的顺序存储,空间复杂度高,不适合存储稀疏图
而邻接表法使用顺序+链式存储的方式,表示方式并不唯一(与树的孩子表示法相似)
//邻接表
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;

与邻接矩阵对比:

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

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

空间复杂度为:O(|V|+|E|)
顺着绿色路线能找到顶点所有的出边
顺着橙色路线能找到顶点所有的入边
邻接多重表
用邻接矩阵以及邻接表存储无向图时,都有所缺陷:

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

空间复杂度:O(|V|+|E|)
删除边、删除节点等操作很方便
分析对比

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

**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)
实现思路:

#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);//初始化辅助队列Qfor (int i = 0; i < G.vexnum; ++i)//从0号顶点开始遍历{if (!visited[i])//对每个连通分量调用一次BFSBFS(G, i);//vi未访问过,从vi开始BFS}
}//广度优先遍历
void BFS(Graph G, int v) //从顶点v出发,广度优先遍历图G
{visit(v);//访问初始顶点vvisited[v] = true;//对v做已访问标记Enqueue(Q, v);//顶点v入队列Qwhile (!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);//访问顶点wvisited[w] = true;//对w做已访问标记EnQueue(Q, w);//顶点w入队列}}}
}
遍历序列是具有可变性的

对于无向图,调用BFS函数的次数=连通分量数
复杂度分析:


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

利用广度优先生成的树,高度是最小的(因为按最短路径)
应用:解决非带权图的单源最短路径问题
//解决非带权图的单源最短路径问题
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;//路径长度加1EnQueue(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);//访问顶点vvisited[v] = true;//设已访问标记for (w = FirstNeighbor(G, v); w >= 0; w = NextNeighbor(G, v, w)){if (!visited[w]){DFS(G, w); // w为v的尚未访问的邻接顶点}}
}
复杂度分析:


遍历序列不唯一性:

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

对于无向图进行BFS/DFS遍历,调用BFS/DFS次数=连通分量数,对于连通图,只调用一次
对于有向图进行BFS/DFS遍历,调用BFS/DFS次数要具体问题具体分析,若起始顶点到其他各顶点都有路径,只调用一次,对于强连通图,从任一结点出发都只需调用一次BFS/DFS
图的应用
最小生成树
连通图的生成树是包含图中全部顶点的一个极小连通子图。若图中顶点数为n,则它的生成树含有n-1条边,对生成树而言,若砍去它的一条边,则会变成非连通图,若加上一条边则会形成一个回路。
广度优先生成树的高度是小于等于深度优先生成树的高度的。
最小生成树定义:

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

Prim:


最短路径BFS
最短路径问题描述:

利用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;//路径长度加1path[w]=u;//最短路径应从u到wEnQueue(Q, w);//顶点w入队}}}
}

时间复杂度:邻接矩阵O(|V|2) 邻接表O(|V|+|E|)
最短路径Dijkstra
dist[ ]记录从源点v0到其他各顶点当前的最短路径长度,它的初态为:若从v0到vi有弧,则dist[i]为弧上的权值,否则置于∞
path[ ]表示从源点到顶点i之间的最短路径的前驱结点。在算法结束时,可根据其值追溯得到源点v0到顶点vi的最短路径。

不适用于有负权值的带权图
用邻接矩阵以及邻接表时间复杂度都为O(|V|2)
最短路径Floyd算法
算法思路:


最终实现:

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

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

有向无环图
若一个有向图中不存在环,则称为有向无环图,简称DAG图
有向无环图是描述含有公共子式的表达式的有效工具
其表示表达式中顶点中不可能出现重复的操作数
步骤:

表示出来的结果可能不唯一
拓扑排序
AOV网:用DAG图表示一个工程,顶点表示活动,有向边<Vi,Vj>表示活动Vi必须先于活动Vj进行,不能存在环路!
拓扑排序:


实现代码:
#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;//输出顶点ifor (p = G.vertices[i].firstarc; p; p = p->nextarc){//将所有i指向的顶点的入度减1,并且将入度为0的顶点压入栈Sv = p->adjvex;if (!(--indegree[v]))//若为0{Push(S, v);//入栈}}}if (count < G.vexnum){return false;//排序失败,有向图中有回路}elsereturn true;//拓扑排序成功
}

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

也可以用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);//访问顶点vvisited[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网。
性质:

AOE网中仅有一个入度为0的顶点,称为开始顶点(源点),也仅有一个出度为0的顶点,称为结束顶点(汇点)
从源点到汇点的有向路径可能有多条,所有路径中,具有最大路径长度的路径称为关键路径,而把关键路径上的活动称为关键活动
完成整个工作的最短时间就是关键路径的长度,若关键活动 不能按时安成为,则整个工程的完成时间就会延长。
几个概念:



求关键路径的步骤:

求事件的最早发生时间:

求事件的最迟发生时间:

求活动的最早发生时间:

求活动的最迟发生时间:

求活动的时间余量:

最终得出关键路径:

特性:
若关键活动耗时增加,则整个工程的工期将增长,缩短关键活动的时间,可以缩短整个工程的工期,当缩短到一定程度时,关键活动可能会变成非关键活动。
可能有多条关键路径,只提高一条关键路径上的关键活动速度并不能缩短整个工程的工期,只有加快那些包括在所有关键路径上的关键活动才能达到缩短工期的目的。
思路总结:

主要参考:王道考研课程
后续会持续更新考研408部分的学习笔记,欢迎关注。
github仓库(含所有相关源码):408数据结构笔记
相关文章:
【数据结构】24王道考研笔记——图
六、图 目录 六、图定义及基本术语图的定义有向图以及无向图简单图以及多重图度顶点-顶点间关系连通图、强连通图子图连通分量强连通分量生成树生成森林边的权、带权网/图特殊形态的图 图的存储及基本操作邻接矩阵邻接表法十字链表邻接多重表分析对比图的基本操作 图的遍历广度…...
zabbix钉钉报警
登录钉钉客户端,创建一个群,把需要收到报警信息的人员都拉到这个群内. 然后点击群右上角 的"群机器人"->"添加机器人"->"自定义", 记录该机器人的webhook值。 添加机器人 在钉钉群中,找到只能群助手 添加机器人 选择自定义机…...
Spring 源码解读
1、Spring 的结构组成 1.1、核心类介绍 Spring 中有两个最核心的类 1 DefaultListableBeanFactory XmlBeanFactory 继承自 DefaultListableBeanFactory,而DefaultListableBeanFactory 是整个 bean加载的核心部分,是 Spring 注册及加载 bean 的默认实现…...
练习时长两年半的网络安全防御“first”
1.网络安全常识及术语 下边基于这次攻击演示我们介绍一下网络安全的一些常识和术语。 资产 任何对组织业务具有价值的信息资产,包括计算机硬件、通信设施、 IT 环境、数据库、软件、文档资料、信息服务和人员等。 网络安全 网络安全是指网络系统的硬件、软件及…...
HttpRunner自动化测试之响应中文乱码处理
响应中文乱码: 当调用接口,响应正文返回的中文是乱码时,一般是响应正文的编码格式不为 utf-8 导致,此时需要根据实际的编码格式处理 示例: 图1中 extract 提取title标题,output 输出 title 变量值&#x…...
idea使用命令将jar包导入到maven仓库中
因为今天突然忘了命令,记下来方便以后查看 pom文件的依赖 jar包路径 进入idea中命令窗 输入命令 mvn install:install-file -DfileD:\Project\spring-cloud\dubbo-api\target\dubbo-api-1.0-SNAPSHOT.jar -DgroupIdcom.wmx -DartifactIddubbo-api -Dversion1.0…...
zookeeper学习(一) Standalone模式(单机模式)安装
安装准备 centos7环境jdk1.8环境zookeeper安装包 安装jdk 上传jdk安装包解压安装包到目录中 tar -zxvf jdk-8u361-linux-x64.tar.gz如果需要指定目录可以在后面加上 -C,如 tar -zxvf jdk-8u361-linux-x64.tar.gz -C 目录配置jdk环境变量 vim /etc/profile打开…...
native webrtc支持切换音频采集设备和获取裸流
https://www.yuque.com/caokunchao/rtendq/oq8w3qgs3g59whru 前言 版本webrtc m96 1、修改webrtc m96代码,向外提供一个adm指针的接口出来 2、外部来获取指针进行设备的选择 3、外部获取音频裸流,麦克风或者扬声器的数据 修改webrtc代码 1、修改H:\w…...
HR怎么看待PMP证书呢?
在当今竞争激烈的职场环境中,拥有专业的证书已经成为了许多人提升职业竞争力的必要途径。PMP证书作为项目管理领域的国际认证,备受HR和企业的青睐。那么,HR在招聘和评估员工时,究竟是如何看待PMP证书的呢? 首先&#x…...
API接口:如何通过使用手机归属地查询
随着手机普及率的不断增加,手机号码的信息查询也成为了一个非常实用的功能。本文将介绍如何通过使用手机归属地查询API接口实现查询手机号码所在地的功能。 首先,我们需要一个可以查询手机号码所在地的API接口。目前市面上有很多免费或付费的API接口可供…...
小创业公司死亡剧本
感觉蛮真实的;很多小创业公司没有阿里华为的命,却得了阿里华为的病。小的创业公司要想活无非以下几点: 1 现金流,现金流,现金流; 2 产品,找痛点,不要搞伪需求; 3 根据公司…...
国产化的接口测试、接口自动化测试工具Apipost的介绍及使用
Apipost介绍: Apipost是 API 文档、API 调试、API Mock、API 自动化测试一体化的研发协作赋能平台,它的定位 Postman Swagger Mock JMeter。 Apipost 是接口管理、开发、测试全流程集成工具,能支撑整个研发技术团队同平台工作࿰…...
【MySQL】不允许你不知道如何插入数据
🎬 博客主页:博主链接 🎥 本文由 M malloc 原创,首发于 CSDN🙉 🎄 学习专栏推荐:LeetCode刷题集 🏅 欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正࿰…...
Vue 渲染流程详解
在 Vue 里渲染一块内容,会有以下步骤及流程: 第一步,解析语法,生成AST 第二步,根据AST结果,完成data数据初始化 第三步,根据AST结果和DATA数据绑定情况,生成虚拟DOM 第四步&…...
10分钟内入门 ArcGIS Pro
本文来源:GIS荟 大家好,这篇文章大概会花费你10分钟的时间,带你入门 ArcGIS Pro 的使用,不过前提是你有 ArcMap 使用经验。 我将从工程文件组织方式、软件界面、常用功能、编辑器、制图这5个维度给大家介绍。 演示使用的 ArcGI…...
【ribbon】Ribbon的使用与原理
负载均衡介绍 负载均衡(Load Balance),其含义就是指将负载(工作任务)进行平衡、分摊到多个操作单元上进行运行,例如FTP服务器、Web服务器、企业核心应用服务器和其它主要任务服务器等,从而协同…...
axios封装到reques.js文件中
封装到js中,避免每次都import 然后写一大堆 import axios from axios /* 可复用的发 ajax 请求的函数: axios */ let baseURLhttp://localhost:3000/ export default function promiseAjax(url,methodget,datanull,params) {return new Promise((resolve, reject) …...
学好Elasticsearch系列-核心概念
本文已收录至Github,推荐阅读 👉 Java随想录 文章目录 节点角色master:候选节点data:数据节点Ingest:预处理节点ml:机器学习节点remote_ cluster_ client:候选客户端节点transform:…...
扩展点都不知道不要说你用了Spring Boot
文章目录 前言1.扩展点1.1. 应用程序生命周期扩展点1.1.1 SpringApplicationRunListener1.1.2 ApplicationEnvironmentPreparedEvent1.1.3 ApplicationPreparedEvent1.1.4 ApplicationStartedEvent1.1.5 ApplicationReadyEvent1.1.6 ApplicationFailedEvent 1.2. 容器扩展点1.2…...
LangChain大型语言模型(LLM)应用开发(五):评估
LangChain是一个基于大语言模型(如ChatGPT)用于构建端到端语言模型应用的 Python 框架。它提供了一套工具、组件和接口,可简化创建由大型语言模型 (LLM) 和聊天模型提供支持的应用程序的过程。LangChain 可以轻松管理与语言模型的交互&#x…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)
1.获取 authorizationCode: 2.利用 authorizationCode 获取 accessToken:文档中心 3.获取手机:文档中心 4.获取昵称头像:文档中心 首先创建 request 若要获取手机号,scope必填 phone,permissions 必填 …...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...
