NO.10数据结构图|Prim算法|Kruskal算法|Dijkstra算法|Floyd算法|拓扑排序|关键路径

图的基本应用

最小生成树

定义

生成树: 生成树是针对无向连通图而言的, 如果一棵树包含一个无向连通图的所有顶点, 那么我们把它叫做这个图的生成树。 (如我们之前提到的深度优先生成树和 广度优先生成树)
生成树有以下性质:

  1. 这棵树是一个连通图。
  2. 如果去掉这棵树的一条边, 它会变成非连通图。
  3. 如果增加一条边, 会形成图中的一条回路。
    生成树示意图:
    ![[Pasted image 20251001114244.png]]

最小生成树: 对于一个带权连通无向图 G = (V, E), 生成树不同, 每棵树的权(即树中所有边上的权值之和) 也可能不同, 我们把权值最小的那棵树叫做该图的最小生成树。
如图, 生成树 1 和生成树 2 都是最小生成树。
![[Pasted image 20251001114305.png]]

由上图我们可以知道, 最小生成树不唯一, 但权值唯一。
当图中各边权值互不相等 --> 其生成树唯一(反之不成立) 。
(因为图当中权值相等的边不一定会变成最小生成树的一部分)
通常我们可以用两种方法得到最小生成树: Prim 算法和 Kruskal 算法

Prim 算法

Prim 算法构造最小生成树的算法思路是:

  1. 维护一个集合 s, 初始为空;
  2. 从图中任选一点, 加入集合 s;
  3. 如果图中所有结点都已经在集合 s 中, 则算法结束, 集合中的点和边共同组成该图的最小生成树; 否则, 进入第 4 步;
  4. 从图中选择一个到当前集合 s 中的结点距离最近的结点, 连同这条边加入到集合 s 中, 到第 3 步。
算法过程示意

![[Pasted image 20251001114420.png]]

算法时间复杂度

每个轮次都需要从图中选择一个到当前集合 s 中的结点距离最近的结点,需要进行|V|个轮次, 所以 Prim 算法的时间复杂度为 O(|V|^2), 不依赖于|E|, (其中|V|指图中顶点的个数, |E|指图中边的个数) , 所以该算法适用于求解边稠密的图的最小生成树。

Kruskal 算法(克鲁斯卡尔算法)

Kruskal 算法构造最小生成树的思路是:

  1. 维护一个集合 s, 将图中所有顶点加入集合 s。
  2. 集合中的点和边会构成若干个连通分量(初始时, 每个顶点都是一个单独的连通分量) 。
  3. 若集合中只有一个连通分量, 则算法终止, 该连通分量就是这个图的生成树; 否则到第 4 步。
  4. 从当前未选取过的边中, 选取权值最小、 两个端点(顶点) 落在集合 s 中不同的连通分量上的一条边, 加入集合 s。 转到第 3 步。
算法过程示意

![[Pasted image 20251001114614.png]]

算法时间复杂度

Kruskal 算法的时间复杂度为 O(|E|log|E|), 依赖于|E|而非|V|(其中, |V|指图中顶点的个数, |E|指图中边的个数) , 所以 Kruskal 算法适合边稀疏而顶点较多的图。

Dijkstra 算法(迪杰斯特拉算法)

最短路径
  • 带权路径长度:
    带权图中, 从一个顶点到另一个顶点所经历的边的权值之和, 叫做该路径的带权路径长度。
  • 最短路径:
    从一个顶点到另一个顶点可能不止一条路径, 带权路径长度最小的那条叫做最短路径。
    ![[Pasted image 20251001114712.png]]
定义

单源最短路径问题: 求图中某一顶点到其他各顶点的最短路径。
求解单源最短路径问题通常采用 Dijkstra 算法。

算法思路

Dijkstra 算法思路如下(顶点总数为 n) :

  1. 维护两个数组:
    dist[]: dist[i]表示源点到顶点 i 的当前已知的最短路径长度。 初始时, 将 dist[]的每个元素值设为∞ (源点处为 0)。
    path[]path[i]表示从源点到顶点 i 的最短路径上, 顶点 i 的前驱顶点。 初始均为-1.

算法思路如下(顶点总数为 n) :

  1. 集合 s 中存放已经求得最短路径的顶点, 初始为空, 然后将源点加入集合 s;
  2. 如果所有顶点都加入了集合 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
  3. 选取{dist[v] | v = 1, 2, ..., n)}中的最小值, 将对应的顶点 v 加入集合 s, 跳转到步骤 2。
    ![[Pasted image 20251001115216.png]]

![[Pasted image 20251001115232.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]]

核心思想:递推求解 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(n1), 其中A(−1)A^{(-1)}A(1)表示原图的邻接矩阵。 不断地通过绕行 VKV_{K}VK顶点去计算当前路径的路径长度。
算法步骤:

  1. 初始化矩阵A(−1)A^{(-1)}A(1)
  2. 求解
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值