数据结构(17)
目录
一、拓扑排序
二、关键路径
三、最短路径
1、最短路径的核心概念与分类
(1)基本定义
(2)关键前提
(3)问题分类
2、单源最短路径算法(从 A 到所有顶点)
(1)Dijkstra 算法(权值非负的图)
(2)Bellman-Ford 算法(含负权边,无负权回路)
3、多源最短路径算法(所有顶点对)
4、最短路径的应用场景
5、算法选择总结
一、拓扑排序
概念:对一个有向无环图中的顶点排成一个具有前后次序的线性序列
方法:
① 输入AOV网络,令 n 为顶点个数
② 在AOV网络中选一个没有直接前驱的顶点,并输出
③ 从图中删去该顶点,同时删去所有它发出的有向边
④ 重复以上②③ 步,直到:
a. 全部顶点均已输出,拓扑有序序列形成,拓扑排序完成
b. 图中还有未输出的顶点,但已跳出处理循环。说明图中还剩下一些顶点,它们都有直接前驱,再也找不到没有前驱的顶点了。这时AOV网络中必存在有向环。
二、关键路径
概念:完成整个工程所需的时间取决于从源点到汇点的最长路径长度,即在这条路径上所有活动的持续时间之和。这条路径长度最长的路径就叫观景路经。
AOE网中:顺序进行;并行进行
三、最短路径
在带权有向图中A点(源点)到达B点(终点)的多条路径中,寻找一条各边权值之和最小的路径,即最短路径。
在带权有向图中,从源点(A 点)到终点(B 点)的最短路径是指 “所有从 A 到 B 的路径中,各边权值之和最小的路径”。这里的 “最短” 并非指路径包含的边数最少,而是权值总和最小(权值可表示距离、时间、成本等实际含义)。求解最短路径是图论中的核心问题,根据图的特性(如权值是否为负、是否有环)和需求(单源最短路径、多源最短路径),有多种经典算法。
1、最短路径的核心概念与分类
(1)基本定义
- 路径的权值和:一条路径 P=(v0,v1,...,vk) 的权值和为各边权值之和,即 W(P)=∑i=0k−1weight(vi,vi+1)。
 - 最短路径:从源点 A 到终点 B 的所有路径中,权值和 W(P) 最小的路径,其权值和称为 “最短距离”(即使存在多条路径权值和相同,均视为最短路径)。
 
(2)关键前提
- 若图中存在负权回路(从某顶点出发,沿回路回到该顶点的权值和为负),且该回路在从 A 到 B 的某条路径上,则不存在最短路径(可通过循环绕路无限减小权值和)。
 - 因此,最短路径算法通常假设图中无负权回路(或负权回路不在源点到终点的路径中)。
 
(3)问题分类
- 单源最短路径:求从一个固定源点到其他所有顶点的最短路径(如从 A 到 B、A 到 C、A 到 D 等)。
 - 单对顶点最短路径:仅求从源点 A 到特定终点 B 的最短路径(可视为单源问题的特例)。
 - 多源最短路径:求图中所有 pairs (u,v) 之间的最短路径(如任意两点间的最短距离)。
 
2、单源最短路径算法(从 A 到所有顶点)
(1)Dijkstra 算法(权值非负的图)
-  
适用场景:带权有向图(或无向图),所有边的权值 非负(weight≥0)。
 -  
核心思想:贪心策略 —— 从源点出发,逐步确定 “已找到最短路径的顶点集”,每次选择 “当前距离源点最近的未确定顶点”,并用该顶点优化其邻接顶点的距离。
 -  
步骤:
- 初始化: 
- 设源点为 s,数组 dist[] 存储各顶点到 s 的当前最短距离(初始 dist[s]=0,其他顶点 dist[v]=∞)。
 - 数组 visited[] 标记顶点是否已确定最短路径(初始全为 false)。
 
 - 迭代更新: 
- 从 visited[] 为 false 的顶点中,选择 dist[v] 最小的顶点 u,标记 visited[u]=true(确定其最短路径)。
 - 对 u 的所有邻接顶点 v,若 dist[v]>dist[u]+weight(u,v),则更新 dist[v]=dist[u]+weight(u,v)(通过 u 到达 v 更近)。
 
 - 终止:重复步骤 2,直至所有顶点均被标记(visited[] 全为 true),此时 dist[] 存储源点到各顶点的最短距离。
 
 - 初始化: 
 -  
示例:带权有向图:A→B(权 2),A→C(权 5),B→C(权 1),B→D(权 6),C→D(权 3)。源点为 A:
- 初始 dist=[0,∞,∞,∞](A:0, B:∞, C:∞, D:∞)。
 - 选 A(dist=0),更新邻接顶点:B=2,C=5。
 - 选 B(dist=2),更新邻接顶点:C=min(5,2+1)=3,D=2+6=8。
 - 选 C(dist=3),更新邻接顶点:D=min(8,3+3)=6。
 - 选 D(dist=6),结束。
 - 结果:A→B→C→D 是最短路径,权值和 6。
 
 -  
特点:
- 时间复杂度:用优先队列优化后为 O((n+e)logn)(n 顶点数,e 边数),适合稀疏图。
 - 局限性:无法处理负权边(因负权边可能导致已确定的最短路径被后续更短路径推翻)。
 
 
(2)Bellman-Ford 算法(含负权边,无负权回路)
-  
适用场景:带权有向图,可含负权边,但无负权回路(否则最短路径不存在)。
 -  
核心思想:松弛操作 —— 通过最多 n−1 次迭代(n 为顶点数),逐步松弛所有边,更新顶点的最短距离;最后检查是否存在可松弛的边(判断负权回路)。
 -  
步骤:
- 初始化:dist[s]=0,其他 dist[v]=∞。
 - 松弛迭代: 
- 对所有边 (u,v) 执行 n−1 次:若 dist[v]>dist[u]+weight(u,v),则更新 dist[v]=dist[u]+weight(u,v)。
 - (原理:最短路径最多含 n−1 条边,n−1 次迭代可确保所有路径被松弛)。
 
 - 负权回路检测: 
- 第 n 次迭代中,若仍存在可松弛的边 (u,v),则图中存在负权回路(因 n 条边的路径必含环,且环为负权)。
 
 
 -  
特点:
- 时间复杂度:O(n⋅e)(简单实现),适合边数较少的图。
 - 优势:可处理负权边,且能检测负权回路。
 
 
3、多源最短路径算法(所有顶点对)
Floyd-Warshall 算法(权值可正可负,无负权回路)
-  
适用场景:带权有向图,可含负权边,但无负权回路,需一次性求所有顶点对之间的最短路径。
 -  
核心思想:动态规划 —— 通过中间顶点优化路径,即 “从 i 到 j 的最短路径,要么直接走 i→j,要么经过中间顶点 k 走 i→k→j”。
 -  
步骤:
- 初始化距离矩阵:dist[i][j] 表示 i 到 j 的初始距离(直接边的权值,无直接边则为 ∞,dist[i][i]=0)。
 - 动态规划迭代: 
- 对每个中间顶点 k(从 0 到 n−1): 
- 对所有顶点对 (i,j):dist[i][j]=min(dist[i][j],dist[i][k]+dist[k][j])。
 
 
 - 对每个中间顶点 k(从 0 到 n−1): 
 - 结果:迭代结束后,dist[i][j] 即为 i 到 j 的最短距离。
 
 -  
特点:
- 时间复杂度:O(n3)(n 为顶点数),适合顶点数较少的图(如 n<400)。
 - 优势:实现简单(三重循环),可处理负权边,一次性解决所有顶点对的最短路径。
 
 
4、最短路径的应用场景
- 导航系统:计算两地之间的最短驾驶距离或最少时间(权值为距离 / 时间)。
 - 网络路由:数据包从源节点到目标节点的最优传输路径(权值为延迟 / 带宽)。
 - 物流规划:从仓库到多个配送点的最低运输成本路径(权值为运输成本)。
 - 游戏 AI:角色在地图中寻找从起点到终点的最短移动路径(权值为步数 / 能耗)。
 
5、算法选择总结
| 场景 | 推荐算法 | 时间复杂度 | 优势 | 
|---|---|---|---|
| 单源,权值非负 | Dijkstra | O((n+e)logn) | 高效,适合稀疏图 | 
| 单源,含负权边(无负环) | Bellman-Ford | O(n⋅e) | 可处理负权边,检测负环 | 
| 多源,所有顶点对 | Floyd-Warshall | O(n3) | 实现简单,适合顶点少的图 | 
