考研408数据结构|最短路径|BFS|Dijkstra|Floyd
这几个算法真的太容易混了😭
尤其是看到"最短路径"四个字就开始纠结:
到底用BFS?Dijkstra?还是Floyd?
我把这几个算法按照**「能不能带权 → 求几对顶点 → 怎么写」**整理了一遍,考试直接按这个思路判断!
🧠 一、先记住:最短路径算法就这三个核心
408里最常见的最短路径算法:
| 算法 | 适用图 | 求什么 |
| 🚶 BFS | 无权图 | 单源最短路径 |
| 🚗 Dijkstra | 带权图,边权非负 | 单源最短路径 |
| 🌐 Floyd | 带权/无权都可以 | 任意两点之间最短路径 |
一句话记忆👇
无权 → BFS
有权 → Dijkstra
所有点两两之间 → Floyd
⚠️ 注意一个特别容易考的点:
Dijkstra不能处理存在负权边的图!
🚶 二、BFS为什么可以求无权图最短路径?
因为BFS是一层一层往外扩展的。
假设从源点 0 出发:
0
/ \
1 2
/ \
3 4
BFS访问顺序大概就是:
0 → 1、2 → 3、4
也就是说:
- 第0层:距离0
- 第1层:距离1
- 第2层:距离2
- 第3层:距离3
所以:
BFS第一次访问到某个顶点时,得到的就是它距离源点的最短边数。
这就是为什么无权图求最短路径可以直接用BFS。
✍️ BFS求最短路径怎么写?
核心就是两个数组:
dist\[\]:记录源点到各顶点的最短距离
path\[\]:记录每个顶点是从谁走过来的
初始化:
disti = -1
pathi = -1
源点:
dists = 0
然后把源点加入队列。
BFS循环:
每次从队列中取出一个顶点 u:
遍历 u 的所有邻接点 v:
如果:
distv == -1
说明 v还没有被访问过。
那么:
distv = distu + 1
pathv = u
然后:
v入队
最后通过 path\[\] 就可以把最短路径倒推出来。
🚗 三、Dijkstra:带权图最短路径的王者
如果图变成这样:
2
0 ─────→ 1
│ │
5│ │1
↓ ↓
2 ─────→ 3
2
这时候就不能简单用BFS了。
因为:
边有不同的权值,走一步不代表距离只增加1。
这时候就轮到:
⭐ Dijkstra算法
它解决的是:
一个源点 → 其他所有顶点的最短路径
也就是:
单源最短路径。
📝 Dijkstra最重要的三个数组
和你笔记里整理的一样,可以重点记:
dist\[\]
path\[\]
final\[\]
① dist\[\]
记录:
源点到每个顶点当前已知的最短距离
例如:
dist3 = 7
表示目前知道:
源点 → 3 的最短距离是7。
② path\[\]
记录:
最短路径上,该顶点的前驱结点是谁
比如:
path3 = 2
说明:
...... → 2 → 3
所以最后可以利用 path\[\] 把完整路径还原出来。
③ final\[\]
这个非常重要‼️
finali = true
表示:
顶点i的最短距离已经最终确定。
也就是说,后面不会再修改它。
🔥 Dijkstra到底在干什么?
其实就一句话:
每次从"还没确定最短路的顶点"中,挑一个 dist 最小的出来,把它确定下来,然后利用它去更新其他顶点。
这个过程叫:
「选最小 + 松弛」
Dijkstra步骤直接背👇
① 初始化
disti = ∞
pathi = -1
finali = false
源点:
dists = 0
② 选择当前dist最小的顶点
从所有:
finali == false
的顶点中,
找到:
disti
最小的那个。
记作:
u
然后:
finalu = true
③ 用u更新其他顶点
遍历 u 的邻接点 v:
如果发现:
distu + arcuv < distv
那么更新:
distv = distu + arcuv
pathv = u
这一步就是传说中的:
「松弛操作」
④ 重复
重复:
选最小 → 确定 → 松弛
直到所有顶点都确定。
通常重复 n-1次 就可以得到最终结果。
🌐 四、Floyd:直接把"所有点到所有点"一次性算出来
如果题目问:
求任意两个顶点之间的最短路径
这时候Dijkstra就不够方便了。
因为Dijkstra是:
一个源点 → 所有点
如果你要:
1 → 2
1 → 3
1 → 4
2 → 1
2 → 3
2 → 4
......
那就要对每个顶点都跑一次Dijkstra。
这时候可以直接使用:
⭐ Floyd算法
🧠 Floyd的核心思想
Floyd特别适合用一句话理解:
尝试让某个顶点k作为中间点,看经过k是不是比原来的路更短。
例如原来:
i ─────────→ j
10
如果发现:
i → k = 3
k → j = 4
那么:
i → k → j = 3 + 4 = 7
比原来的10更短。
于是更新:
distij = 7
也就是:
distij
=
min(distij,
distik + distkj)
🔥 这就是Floyd最核心的公式!
📌 Floyd为什么是三个for循环?
因为它要依次考虑:
k = 0,1,2,...,n-1
作为中间点。
核心代码逻辑就是:
for k
for i
for j
distij =
min(distij,
distik + distkj)
所以时间复杂度:
⏱️ O(n³)
⚡ 五、三种算法到底怎么选?
这张表建议直接保存👇
| 题目条件 | 算法 |
| 无权图 + 一个源点 | BFS |
| 带权图 + 一个源点 | Dijkstra |
| 带权图 + 任意两点 | Floyd |
| 无权图 + 任意两点 | Floyd也可以 |
| 存在负权边 | ❌ 不能用Dijkstra |
🚨 六、408最容易掉进去的几个坑
❌ 坑1:最短路径都用Dijkstra
不是!
无权图优先想到BFS。
❌ 坑2:Dijkstra能处理负权边
不能。
Dijkstra的重要前提:
边权不能为负。
❌ 坑3:Floyd只能用于带权图
也不是。
无权图也可以使用,只需要把:
有边 → 1
无边 → ∞
即可。
❌ 坑4:BFS只能求遍历顺序
错!
BFS除了遍历,还可以解决:
无权图的单源最短路径问题。
🧩 七、最后给你一个"考场判断口诀"
看到最短路径题,先别急着写代码。
先问自己三个问题:
① 有没有权值?
没有权值 → BFS
② 有权值,但是问一个源点到其他所有点?
Dijkstra
③ 问任意两个顶点之间?
Floyd
直接记:
🟢 无权看BFS
🔵 带权看Dijkstra
🟣 全点看Floyd
📖 408最短路径,真正要掌握的其实就这几件事
BFS:
dist\[\]
path\[\]
队列
逐层扩展
Dijkstra:
dist\[\]
path\[\]
final\[\]
选最小
松弛
Floyd:
dist\[\]\[\]
k作为中间点
min更新
三重循环
不要一上来就死背代码。
先把"这道题到底应该用谁"判断对,代码基本就已经成功一半了。 🚀
#408考研 #计算机考研 #数据结构 #最短路径 #BFS #Dijkstra #Floyd #算法 #考研408 #数据结构笔记 #考研学习 #计算机考研408