考点频率 :★★★★★(数据结构必考,选择题常考Dijkstra的dist数组变化,下午题偶尔结合图考查算法选边过程)
难度 :⭐⭐⭐⭐⭐(Dijkstra逻辑较绕,Floyd的三重循环是难点)
建议:重点掌握Dijkstra的贪心选点过程和dist数组更新,Floyd掌握三层循环的顺序(k在外层)
1️⃣ 什么是最短路径问题?
最短路径 是图论中最经典的应用问题之一。它分为两种:
| 问题类型 | 含义 | 求解目标 |
|---|---|---|
| 单源最短路径 | 从一个指定顶点到其他所有顶点的最短路径 | 求一组最短路径(一个起点到所有终点) |
| 多源最短路径 | 求图中任意两个顶点之间的最短路径 | 求一个最短路径矩阵(所有起点到所有终点) |
打个比方 :单源最短路径就像高德地图的"去XX"导航 ------你输入起点(你家),地图告诉你到每个目的地的最短路线。多源最短路径就像高德地图的"全国任意两点"导航------你随时查任意两个城市之间的最短路线。
求解两种问题的最经典算法:
- Dijkstra算法 :解决单源最短路径(不能处理负权边)
- Floyd算法 :解决多源最短路径(可以处理负权边,但不能有负权环)
为什么Dijkstra不能处理负权边? Dijkstra的贪心策略是"每次选dist最小的顶点加入已确定集合",一旦加入,就认为它的dist已经是最短的。但如果存在负权边,后续可能通过这条负权边让已经确定的顶点的dist变得更小,破坏了贪心的正确性。
2️⃣ Dijkstra算法(单源最短路径)
2.1 核心思想
Dijkstra算法的策略是:
维护一个"已确定最短距离"的顶点集合 SSS。每次从 V−SV-SV−S 中选择一个距离起点最近的顶点 加入 SSS,然后用这个顶点去"松弛"它的邻接顶点,更新它们到起点的距离。
打个比方 :Dijkstra算法就像向全校公布"离校长室最近的办公室"。校长室是起点,你先把校长室周围的办公室距离都量一遍,找到最近的一间(比如教务处),然后以教务处为跳板,看看有没有"先到教务处再到某处"比"直接从校长室到某处"更近的路线。不断重复这个过程,直到所有办公室的距离都算出来。
2.2 算法步骤
- 初始化:
dist[起点] = 0,其他顶点的dist = ∞ - 循环 n−1n-1n−1 次:
- 从 未被访问 的顶点中,选择
dist值最小 的顶点 uuu - 标记 uuu 为已访问(已确定最短距离)
- 对 uuu 的每个邻接顶点 vvv,如果 vvv 未被访问,执行松弛操作 :
distv=min(distv,distu+w(u,v))distv = \min(distv, distu + w(u, v))distv=min(distv,distu+w(u,v))
- 从 未被访问 的顶点中,选择
2.3 执行过程示例
图:从顶点1出发,求到所有顶点的最短路径
1 ------(4)------ 2
/ \ / \
(1) (2) (2) (3)
/ \ / \
3 ------(5)------ 4 ------(1)------ 5
dist数组变化(∞表示无穷大):
| 步骤 | 选中的顶点 | 已访问集合 | dist1 | dist2 | dist3 | dist4 | dist5 | 松弛操作说明 |
|---|---|---|---|---|---|---|---|---|
| 初始 | --- | ∅ | 0 | ∞ | ∞ | ∞ | ∞ | 起点为1 |
| 1 | 1 | {1} | 0 | 4 | 1 | 2 | ∞ | 通过1,dist3=1, dist4=2, dist2=4 |
| 2 | 3(dist=1) | {1,3} | 0 | 4 | 1 | 2 | ∞ | 通过3,3-4权5,dist4不变(2<1+5=6) |
| 3 | 4(dist=2) | {1,3,4} | 0 | 4 | 1 | 2 | 3 | 通过4,4-2权2,dist2不变;4-5权1,dist5=3 |
| 4 | 5(dist=3) | {1,3,4,5} | 0 | 4 | 1 | 2 | 3 | 通过5,5-2权3,dist2不变(4<3+3=6) |
| 5 | 2(dist=4) | {1,3,4,5,2} | 0 | 4 | 1 | 2 | 3 | 全部访问,结束 |
最终最短路径:1→3(1),1→4(2),1→5(3),1→2(4)
2.4 时间复杂度与适用场景
| 存储方式 | 时间复杂度 | 说明 |
|---|---|---|
| 邻接矩阵 | O(n2)O(n^2)O(n2) | 适合稠密图 |
| 邻接表 + 二叉堆 | O(elogn)O(e \log n)O(elogn) | 适合稀疏图 |
| 邻接表 + 斐波那契堆 | O(e+nlogn)O(e + n \log n)O(e+nlogn) | 最优但复杂 |
限制 :Dijkstra算法不能处理带有负权边的图(贪心策略失效)。
3️⃣ Floyd算法(多源最短路径)
3.1 核心思想
Floyd算法的策略是:
动态规划。依次考虑每个顶点作为"中转点",看看通过这个中转点能否让任意两个顶点之间的距离变得更短。
打个比方 :Floyd算法就像查全国航班最短中转路线。你一开始只考虑直达航班(没有中转),然后逐渐放开限制------允许经过城市A中转,再允许经过城市B中转,直到所有城市都可以作为中转站。最终得到的矩阵就是所有城市之间的最短飞行距离。
3.2 算法步骤(三层循环)
核心代码逻辑(伪代码):
c
// 初始化 dist[i][j] = 边权(直接连接)或 ∞(不直接连接)
// 初始化 dist[i][i] = 0
for (int k = 0; k < n; k++) { // 外层:中转点
for (int i = 0; i < n; i++) { // 中层:起点
for (int j = 0; j < n; j++) { // 内层:终点
if (dist[i][k] + dist[k][j] < dist[i][j]) {
dist[i][j] = dist[i][k] + dist[k][j];
}
}
}
}
⚠️ 重要 :中转点 kkk 必须放在最外层 。如果 kkk 放在内层,会导致某些路径组合被遗漏(因为中转点的考虑顺序会影响能否组合出完整路径)。Floyd算法的动态规划递推关系决定了 kkk 必须在最外层,这一点考试中经常考查。
3.3 执行过程示例
图:4个顶点,边权如下
初始距离矩阵 D(0)D^{(0)}D(0):
| 顶点 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|
| 1 | 0 | 3 | ∞ | 7 |
| 2 | 3 | 0 | 1 | ∞ |
| 3 | ∞ | 1 | 0 | 2 |
| 4 | 7 | ∞ | 2 | 0 |
经过k=1(允许顶点1作为中转点)后:
- 检查所有 i,ji, ji,j,看看走
i → 1 → j是否更短 - 比如 i=2,j=4i=2, j=4i=2,j=4:原来 dist24=∞dist24 = ∞dist24=∞,走 2→1→4=3+7=102→1→4 = 3 + 7 = 102→1→4=3+7=10,更新为10
- 更新后 D(1)D^{(1)}D(1) 中 dist24=10dist24 = 10dist24=10
经过k=2、k=3、k=4逐步更新,最终得到所有顶点之间的最短路径矩阵。
4️⃣ Dijkstra vs Floyd(完整对比表,重点!)
| 对比项 | Dijkstra算法 | Floyd算法 |
|---|---|---|
| 解决的问题 | 单源最短路径(一个起点到所有终点) | 多源最短路径(所有起点到所有终点) |
| 算法思想 | 贪心(每次选dist最小的顶点) | 动态规划(逐步允许更多的中转点) |
| 核心数据结构 | dist数组 + visited数组 | 二维距离矩阵 |
| 核心循环结构 | 单层循环(选点+松弛) | 三层循环(k在最外层) |
| 时间复杂度 | O(n2)O(n^2)O(n2)(邻接矩阵)/ O(elogn)O(e \log n)O(elogn)(堆优化) | O(n3)O(n^3)O(n3) |
| 是否支持负权边 | ❌ 不支持(贪心失效) | ✅ 支持(但不能有负权环) |
| 是否支持负权环 | ❌ 不支持 | ❌ 不支持(负权环会导致无限小路径) |
| 空间复杂度 | O(n)O(n)O(n) | O(n2)O(n^2)O(n2) |
| 适用场景 | 单源查询、稠密图或稀疏图均可 | 需要查询任意两点距离、顶点数较少(n≤200n \le 200n≤200) |
5️⃣ 经典例题
例题1(Dijkstra的dist数组):用Dijkstra算法求从顶点A到各顶点的最短路径。以下哪个是算法执行过程中,某个时刻的dist数组状态?
解析 :在Dijkstra执行过程中,dist数组的值会逐步减小(从不大于之前的dist值)。因此,如果某个选项中的dist值比初始值大或出现了"先变小后变大"的情况,就是错误的。
例题2(Floyd的三层循环):下列关于Floyd算法三层循环顺序的说法中,正确的是( )。
A. 起点 iii 在最外层,中转点 kkk 在最内层
B. 终点 jjj 在最外层,起点 iii 在最内层
C. 中转点 kkk 在最外层 ,起点 iii 和终点 jjj 在内层
D. 三层循环的顺序不影响最终结果
解析 :Floyd算法的关键是中转点 kkk 在最外层 。如果顺序错误,动态规划的状态转移就无法正确完成。选 C。
例题3(判断):Floyd算法可以处理包含负权环的图。( )
解析 :错误。如果图中存在负权环,任意两个顶点之间的最短路径会趋向于 −∞-\infty−∞,没有有限的最短路径。
6️⃣ 记忆口诀
Dijkstra求单源,贪心选点逐步松。
邻接矩阵 O(n2)O(n^2)O(n2),负权边下不能碰。
Floyd求多源,动态规划三重圈。
中转点 kkk 在最外,顶点不多用它行。
7️⃣ 小测验(评论区对答案)
用Dijkstra算法求从顶点1到其他顶点的最短路径,以下关于算法执行过程的描述中,正确的是( )。
A. 每次从所有顶点中选择dist值最小的顶点加入已访问集合
B. 每次从未被访问 的顶点中选择dist值最小的顶点加入已访问集合
C. 算法执行过程中,已访问顶点的dist值可能会被更新
D. Dijkstra算法可以处理负权边
🔔 本专栏日更,点击头像 → 专栏《软考中级高频考点》订阅,第一时间接收新内容
#软考中级 #软件设计师 #最短路径 #Dijkstra #Floyd #数据结构 #软考备考