【数据结构】图的应用:最短路径(Dijkstra / Floyd)

考点频率 :★★★★★(数据结构必考,选择题常考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 算法步骤

  1. 初始化:dist[起点] = 0,其他顶点的 dist = ∞
  2. 循环 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(elog⁡n)O(e \log n)O(elogn) 适合稀疏图
邻接表 + 斐波那契堆 O(e+nlog⁡n)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(elog⁡n)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 #数据结构 #软考备考

相关推荐
linux-hzh1 小时前
百日算法修炼 · Day 13
算法
如此这般英俊1 小时前
手搓Claude Code-第十三章 background_tasks
前端·人工智能·chrome·python·算法·语言模型·自然语言处理
Fa_Mian_Tuan1 小时前
图论基础 | 邻接多重表原理剖析 + 完整可运行C语言代码(插边/删边/释放)
c语言·数据结构·笔记·算法·图论
学习星球1 小时前
【LeetCode算法题精讲】二分查找精讲
java·数据结构·算法·leetcode·职场和发展·图搜索
邵奈一1 小时前
Ornith 1.5 开源即王炸:会自我进化的大模型 Mac 本地部署实测
算法
民乐团扒谱机1 小时前
【微实验】线性代数之——奇异值分解 SVD:穿透数据迷雾的“X光机”,附 MATLAB 全流程仿真
线性代数·算法·matlab
windliang1 小时前
Agent 开发到底做什么:Agent、Harness 基础概念
人工智能·算法·程序员
Eloudy1 小时前
Ubuntu 22.04 从源码编译安装 ROS 2 Jazzy 笔记
linux·笔记·ubuntu
arin8761 小时前
【图论】网络流
算法