图的基本应用
最小生成树
定义
生成树: 生成树是针对无向连通图而言的, 如果一棵树包含一个无向连通图的所有顶点, 那么我们把它叫做这个图的生成树。 (如我们之前提到的深度优先生成树和 广度优先生成树)
生成树有以下性质:
- 这棵树是一个连通图。
- 如果去掉这棵树的一条边, 它会变成非连通图。
- 如果增加一条边, 会形成图中的一条回路。
生成树示意图:
![![[Pasted image 20251001114244.png]]](https://i-blog.csdnimg.cn/direct/d273f9ca9932402790ec77c07629bc82.png)
最小生成树: 对于一个带权连通无向图 G = (V, E), 生成树不同, 每棵树的权(即树中所有边上的权值之和) 也可能不同, 我们把权值最小的那棵树叫做该图的最小生成树。
如图, 生成树 1 和生成树 2 都是最小生成树。
![![[Pasted image 20251001114305.png]]](https://i-blog.csdnimg.cn/direct/c3b14b80434d4141a6bc9fc5f9f398aa.png)
由上图我们可以知道, 最小生成树不唯一, 但权值唯一。
当图中各边权值互不相等 --> 其生成树唯一(反之不成立) 。
(因为图当中权值相等的边不一定会变成最小生成树的一部分)
通常我们可以用两种方法得到最小生成树: Prim 算法和 Kruskal 算法
Prim 算法
Prim 算法构造最小生成树的算法思路是:
- 维护一个集合 s, 初始为空;
- 从图中任选一点, 加入集合 s;
- 如果图中所有结点都已经在集合 s 中, 则算法结束, 集合中的点和边共同组成该图的最小生成树; 否则, 进入第 4 步;
- 从图中选择一个到当前集合 s 中的结点距离最近的结点, 连同这条边加入到集合 s 中, 到第 3 步。
算法过程示意
![![[Pasted image 20251001114420.png]]](https://i-blog.csdnimg.cn/direct/4b995946ccd74d7b888608cbaafb0eee.png)
算法时间复杂度
每个轮次都需要从图中选择一个到当前集合 s 中的结点距离最近的结点,需要进行|V|个轮次, 所以 Prim 算法的时间复杂度为 O(|V|^2), 不依赖于|E|, (其中|V|指图中顶点的个数, |E|指图中边的个数) , 所以该算法适用于求解边稠密的图的最小生成树。
Kruskal 算法(克鲁斯卡尔算法)
Kruskal 算法构造最小生成树的思路是:
- 维护一个集合 s, 将图中所有顶点加入集合 s。
- 集合中的点和边会构成若干个连通分量(初始时, 每个顶点都是一个单独的连通分量) 。
- 若集合中只有一个连通分量, 则算法终止, 该连通分量就是这个图的生成树; 否则到第 4 步。
- 从当前未选取过的边中, 选取权值最小、 两个端点(顶点) 落在集合 s 中不同的连通分量上的一条边, 加入集合 s。 转到第 3 步。
算法过程示意
![![[Pasted image 20251001114614.png]]](https://i-blog.csdnimg.cn/direct/83be8fefdd174ba79e2946b827ca117b.png)
算法时间复杂度
Kruskal 算法的时间复杂度为 O(|E|log|E|), 依赖于|E|而非|V|(其中, |V|指图中顶点的个数, |E|指图中边的个数) , 所以 Kruskal 算法适合边稀疏而顶点较多的图。
Dijkstra 算法(迪杰斯特拉算法)
最短路径
- 带权路径长度:
带权图中, 从一个顶点到另一个顶点所经历的边的权值之和, 叫做该路径的带权路径长度。 - 最短路径:
从一个顶点到另一个顶点可能不止一条路径, 带权路径长度最小的那条叫做最短路径。
![![[Pasted image 20251001114712.png]]](https://i-blog.csdnimg.cn/direct/0e2b0a7190f54bba842ca6482f536e38.png)
定义
单源最短路径问题: 求图中某一顶点到其他各顶点的最短路径。
求解单源最短路径问题通常采用 Dijkstra 算法。
算法思路
Dijkstra 算法思路如下(顶点总数为 n) :
- 维护两个数组:
dist[]:dist[i]表示源点到顶点 i 的当前已知的最短路径长度。 初始时, 将dist[]的每个元素值设为∞ (源点处为 0)。
path[]:path[i]表示从源点到顶点 i 的最短路径上, 顶点 i 的前驱顶点。 初始均为-1.
算法思路如下(顶点总数为 n) :
- 集合 s 中存放已经求得最短路径的顶点, 初始为空, 然后将源点加入集合 s;
- 如果所有顶点都加入了集合 s, 则算法结束; 否则, 当前加入集合 s 的顶点中, 最后一个加入的是顶点 u, 对于每一个
dist[v](v = 1, 2, …, n 且未加入集合 s) 如果 dist[u]+∣<u,v>∣<dist[v]dist[u]+|<u,v>| < dist[v]dist[u]+∣<u,v>∣<dist[v], 则令 dist[v]=dist[u]+∣<u,v>∣dist[v] =dist[u] + |<u,v>|dist[v]=dist[u]+∣<u,v>∣, path[v]=upath[v] = upath[v]=u。 - 选取
{dist[v] | v = 1, 2, ..., n)}中的最小值, 将对应的顶点 v 加入集合 s, 跳转到步骤 2。
![![[Pasted image 20251001115216.png]]](https://i-blog.csdnimg.cn/direct/f5717a1b737945da90082d12a6948eea.png)
![![[Pasted image 20251001115232.png]]](https://i-blog.csdnimg.cn/direct/5f2638c0c1564802b69bcc7c54e9aa94.png)
算法伪代码
//G为图
//数组dist[i]表示源点a到顶点i的当前已知的最短路径长度。
//数组path[i]表示从源点到顶点i的最短路径上,顶点i的前驱顶点。初始均为-1。
//a为源点
Dijkstra(G, dist[], path[],a){
初始化dist[],将dist[]的每个元素值设为oo(源点处为0);
初始化path[],将path[]的每个元素值设为-1;
for(循环n次){
u = 使dist[u]最小的还未被访问的顶点的标号;
记u已被访问过;
for(从u出发能到达的所有顶点v){
if(v未被访问 && 以u为中介点使顶点a到达顶点v的最短距离dist[v]更优){
更新dist[v];
path[v] = u; //更新path[v]
}
}
}
}
算法复杂度
Dijkstra 算法的时间复杂度为 O(|V|^2) (|V|为图中顶点的个数)
注意: Dijkstra 算法不适用于有负权值边的图。
弗洛依德(Floyd)算法
求解图中各个顶点之间的最短路径问题
![![[Pasted image 20251001115823.png]]](https://i-blog.csdnimg.cn/direct/a2b511d3c5a44422ad65ff8472d6f2e9.png)
核心思想:递推求解 n(顶点个数)阶方阵序列 A(−1),A(0),A(1),…,A(n−1)A^{(-1)},A^{(0)},A^{(1)},\dots,A^{(n-1)}A(−1),A(0),A(1),…,A(n−1), 其中A(−1)A^{(-1)}A(−1)表示原图的邻接矩阵。 不断地通过绕行 VKV_{K}VK顶点去计算当前路径的路径长度。
算法步骤:
- 初始化矩阵A(−1)A^{(-1)}A(−1) ;
- 求解

最低0.47元/天 解锁文章

被折叠的 条评论
为什么被折叠?



