2024年5月26日一稿(王道P220)
2024年6月16日
2024年7月5日
2024年10月28日
2024年11月10日
2024年11月11日(杀出一条血路)
18
19
25
26
27
3
7
🏀6.1 图的基本概念
注意:线性表可以是空表,树可以是空树,但图不可以是空,即V一定是非空集
【数据结构与算法,图的数据结构,动画讲解】 https://www.bilibili.com/video/BV1ET421673Y/?share_source=copy_web&vd_source=0caeacd6c3217ba41c56ea47a129e168
🚀6.1.1 图的定义
若用顶点表示对象,则边表示对象间关系。
2E = V(无向图的度之和 是 边数的两倍)
在有向图中,所有顶点的出度的和=所有顶点的入度的和。
奇数度的顶点的个数是偶数,故奇度顶点的度之和亦偶,
若一个图G有n个顶点,边的条数小于n-1,则G必为非连通图。
相连通(关联)
简单图:不存在 重复 和 没有指向自己的边
🚢完全图
任意两个顶点都存在直接相连的边
🚢 连通图
连通图是指每对顶点之间都有路径的图。换句话说,图中的每个顶点都可以从任何其他顶点到达。
强连通图是一种有向图,其中每个顶点到其他任何顶点都有一条有向路径。换句话说,对于强连通图中的任何两个顶点A和B,都存在从A到B和从B到A的有向路径。
真题
(n-1)(n-2)/2 + 1
存在连通,那么一定不能分开为两个强连通分量
本身就处于一个连通分量里面
🥎6.2 图的存储及基本操作
🚀6.2.1邻接矩阵法
时间复杂度:
- 空间复杂度:O(V^2),其中V是顶点的数量。因为邻接矩阵是一个二维数组,其大小为V×V。
- 插入边:O(1)。在邻接矩阵中,插入一条边(即设置两个顶点之间的连接关系)只需要修改矩阵中的一个元素。
- 查找边:O(1)。同样,查找两个顶点之间是否存在边也只需要访问矩阵中的一个元素。
- 遍历图:O(V^2)。如果要遍历图中所有的边,需要遍历整个矩阵。
入度为列,出度为行(有向图)
【图的存储-邻接矩阵】 https://www.bilibili.com/video/BV18C4y1A7JC/?share_source=copy_web&vd_source=0caeacd6c3217ba41c56ea47a129e168
缺点:
1. 统计边的数量效率较低。
2. 空间复杂度高。
邻接矩阵 | 使用二维数组表示图中顶点之间的连接关系。 若顶点i与顶点j之间有边,则数组元素A[i][j]为1(或边的权值);否则为0。 | 1. 简单直观,易于实现。 2. 方便查找两顶点之间是否存在边。 | 1. 空间复杂度高,对于稀疏图不适用。 2. 矩阵的对称性(无向图)未被有效利用,存在空间浪费。 | 适用于稠密图或需要频繁查询顶点间关系的场 |
🚀6.2.2 邻接表
时间复杂度:
- 空间复杂度:
- 无向图:O(V + 2E数量,E是边的数量。因为每个顶点对应一个链表,链表中的节点数量等于该顶点的度(对于无向图,每个边会出现在两个顶点的链表中)。
- 有向图:O(V + E),因为每条边只出现在一个顶点的链表中。
- 插入边:O(1)(平均情况)。在邻接表中插入一条边通常只需要在对应顶点的链表末尾添加一个节点,但如果需要查找顶点在图中的位置,则时间复杂度可能增加到O(V)(最坏情况)。
- 查找边:O(E)(最坏情况)。如果要查找是否存在一条特定的边,可能需要遍历所有顶点的链表。但在实际应用中,通常通过遍历特定顶点的链表来查找与该顶点相连的边,此时时间复杂度为O(d),其中d是该顶点的度。
- 遍历图:O(V + E)。遍历图中的所有顶点和边,需要遍历每个顶点的链表。
【图的存储-邻接表/邻接表画法】 https://www.bilibili.com/video/BV1Ng4y1Z7yN/?p=4&share_source=copy_web&vd_source=0caeacd6c3217ba41c56ea47a129e168
邻接表 | 每个顶点对应一个链表,链表中的节点表示与该顶点相连的其他顶点。 | 1. 节省空间,适用于稀疏图。 2. 便于添加和删除边。 | 1. 查找顶点之间的边不如邻接矩阵方便。 2. 对于有向图,需要两个邻接表分别表示出边和入边(或采用逆邻接表解决入度问题)。 | 适用于稀疏图或需要高效存储和修改边的场景 |
奇数个是有向图
🚀6.2.3 十字链表
【数据结构——四分钟搞定十字链表】 https://www.bilibili.com/video/BV1Nb411d7tV/?share_source=copy_web&vd_source=0caeacd6c3217ba41c56ea47a129e168
稀疏矩阵的存储方式
顺序存储:三元组表示法
链式存储:邻接表表示法和十字链表法
邻接表
十字链表
十字链表 | 适用于有向图,结合了邻接表和逆邻接表的特点。每个顶点有两个链表,一个表示出边,一个表示入边。 | 1. 便于同时获取顶点的出度和入度。 2. 节省空间,避免边的信息冗余(相对于分别使用邻接表和逆邻接表)。 | 实现相对复杂,需要维护两个链表。 | 适用于需要同时考虑顶点出度和入度的有向图。 |
🚀6.2.4 邻接多重表
【数据结构---邻接多重表 的画法】 https://www.bilibili.com/video/BV1TL411b7V3/?share_source=copy_web&vd_source=0caeacd6c3217ba41c56ea47a129e168
邻接多重表 | 邻接表的一种改进,用于无向图,避免邻接表中边的信息冗余。每条边在邻接多重表中只存储一次。 | 1. 节省空间,避免边的信息冗余。 2. 便于对边进行操作,如删除边。 | 实现相对复杂,需要维护边的双向链表。 | 适用于需要频繁对边进行操作的稀疏无向图 |
🚀6.2.5 图的基本操作
🎱6.3 图的遍历
遍历 不重不漏,所有结点
🚀6.3.1 广度优先搜索
【图的广度优先遍历/广度优先搜索】 https://www.bilibili.com/video/BV1VK411t74h/?share_source=copy_web&vd_source=0caeacd6c3217ba41c56ea47a129e168
队列
空间复杂度:最坏情况,辅助队列大小为 0(V)
邻接矩阵
邻接表 O(V+E)
🚢广度优先生成树
邻接表不同,
生成树不同
🚢广度优先生成森林
🚀6.3.2 深度优先搜索
栈
【图的深度优先遍历/深度优先搜索】 https://www.bilibili.com/video/BV1Qp4y1f7Wg/?share_source=copy_web&vd_source=0caeacd6c3217ba41c56ea47a129e168
🚀6.3.3 图的遍历与图的连通性
🏐6.4 图的应用
生成树
图1是非连通图,故由其连通分量来生成树,图2和图3是图1的连通分量的生成树,且图2和图3构成森林。
连通分量
上图的图1是一个无向非连通图。但是它有两个连通分量,即图2和图3.
而图4尽管是图1的子图,但它却不满足连通子图的极大顶点数。因此它不是图1的无向图的连通分量。
扩大了
n个点
环
入度为0的点/出度为0的点
🚀6.4.1 最小生成树
E = V - 1
中间的边可能也会有多条
【图-最小生成树-Prim(普里姆)算法和Kruskal(克鲁斯卡尔)算法】 https://www.bilibili.com/video/BV1wG411z79G/?share_source=copy_web&vd_source=0caeacd6c3217ba41c56ea47a129e168
最小生成树不唯一,其权值和唯一
有环-->拓扑排序不存在
适合稠密图
适合稀疏图
🚀6.4.2最短路径
【图-最短路径-Dijkstra(迪杰斯特拉)算法】 https://www.bilibili.com/video/BV1uT4y1p7Jy/?share_source=copy_web&vd_source=0caeacd6c3217ba41c56ea47a129e168
求某一个点到其他任何点的最短路径
【图-最短路径-Floyd(弗洛伊德)算法】
https://www.bilibili.com/video/BV19k4y1Q7Gj/?share_source=copy_web&vd_source=0caeacd6c3217ba41c56ea47a129e168
Floyd算法不能解决带有“负权回路”的图(有负权值的边组成回路),这种图有可能没有最短路径
🚀6.4.3 有向无环图描述表达式
【6.4_5有向无环图描述表达式】 https://www.bilibili.com/video/BV1SL411S7H7/?share_source=copy_web&vd_source=0caeacd6c3217ba41c56ea47a129e168
🚀6.4.4 拓扑排序
D
不能确定边点关系
通过DFS生成逆拓扑排序
【图-拓扑排序】 https://www.bilibili.com/video/BV1XV411X7T7/?share_source=copy_web&vd_source=0caeacd6c3217ba41c56ea47a129e168
拓扑排序:在图论中,由一个有向无环图的顶点组成的序列,当且仅当满足下列条件时,称为该图的一个拓扑排序:
每个顶点出现且只出现一次。
若顶点A在序列中排在顶点B的前面,则在图中不存在从顶点B到顶点A的路径。
或定义为:拓扑排序是对有向无环图的顶点的一种排序,它使得若存在一条从顶点A到顶点B的路径,则在排序中顶点B出现在顶点A的后面。每个AOV网都有一个或多个拓扑排序序列。
🚀6.4.5 关键路径
【数据结构——求AOE网中的关键路径】 https://www.bilibili.com/video/BV1JM411v7zu/?share_source=copy_web&vd_source=0caeacd6c3217ba41c56ea47a129e168
对于关键路径,需要注意以下几点:
1)关键路径上的所有活动都是关键活动,它是决定整个工程的关键因素,因此可以通过加快关键活动来缩短整个工程的工期。但也不能任意缩短关键活动,因为一旦缩短到一定程度,该关键活动就可能会变成非关键活动。
2)网中的关键路径并不唯一,且对于有几条关键路径的网,只提高一条关键路径上的关键活动速度并不能缩短整个工程的工期,只有加快那些包括在所有关键路径上的关键活动才能达到缩短工期的目的。
各种图算法在采用邻接矩阵或邻接表存储时的时间复杂度如表6.5所示。
🏀总结
2. 关于图的遍历、连通性、生成树、关键路径的几个要点
1)在执行图的遍历时,因为图中可能存在回路,且图的任意一个顶点都可能与其他顶点相连,所以在访问完某个顶点后可能会沿某些边又回到了曾经访问过的顶点。因此,需要设置一个辅助数组visited[]标记顶点是否已被访问过,避免重复访问。(DFS和BFS)
2)深度优先搜索时利用回溯法对图遍历,一般利用递归方法实现,每当向前递归查找某一邻接结点之前,必须判断该结点是否访问过。另外,递归算法均可借助栈来实现非递归算法,深度优先搜索也不例外
3)广度优先搜索是一种分层的遍历过程,每向前走一步可能访问一批顶点,不像深度优先搜索那样有退回的情况。因此,它不是一个递归的过程。
4)一个给定的图的邻接矩阵表示是唯一的,但对于邻接表来说,若边的输入先后次序不同,则生成的邻接表表示也不同。
5)图的最小生成树首先必须是带权连通图,其次要在n个顶点的图中选择n-1条边将其连通,使得其权值总和达到最小,且不出现回路。
6)加速某一关键活动不一定能缩短整个工程的工期,因为AOE网中可能存在多条关键路径。可能存在称为“桥”的一种特殊关键活动,它位于所有的关键路径上,只有它加速才会缩短整个工期。
最小生成树不唯一,最小生成树边权和唯一
🚢Dijkstra 算法
初试dist数组值都是 结点1 直接到达其余结点的距离
炸眼
没有关系
I:缩短,那么该活动可能不是关键路径了
非关键路径无关于总体工程时间
关键路径延长一定延长
依次求最大
访问节点时机改为return前 输出的就是逆拓扑排序
相当于无权图
可能是
调用次数就是连通分量个数
不唯一
🎱真题
09 完全图 就是边最多的情况下
连通分量: 无向图中的 极大连通子图🎯
(子图必须连通,且包含尽可能多的顶点和边)
称为连通分量有向图至少需要n条边才能强连通🎯(形成回路),至多n(n−1)条
📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌
也可以等于
📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌
任意环路
举反例
参考