📚408数据结构|最短路径算法一篇彻底搞懂!BFS、Dijkstra、Floyd别再傻傻分不清了

4 阅读5分钟

考研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[]:记录每个顶点是从谁走过来的

初始化:

dist[i] = -1

path[i] = -1

源点:

dist[s] = 0

然后把源点加入队列。

BFS循环:

每次从队列中取出一个顶点 u:

遍历 u 的所有邻接点 v:

如果:

dist[v] == -1

说明 v还没有被访问过。

那么:

dist[v] = dist[u] + 1

path[v] = u

然后:

v入队

最后通过 path[] 就可以把最短路径倒推出来。

🚗 三、Dijkstra:带权图最短路径的王者

如果图变成这样:

       2

   0 ─────→ 1

   │        │

  5│        │1

   ↓        ↓

   2 ─────→ 3

       2

这时候就不能简单用BFS了。

因为:

边有不同的权值,走一步不代表距离只增加1。

这时候就轮到:

⭐ Dijkstra算法

它解决的是:

一个源点 → 其他所有顶点的最短路径

也就是:

单源最短路径。

📝 Dijkstra最重要的三个数组

和你笔记里整理的一样,可以重点记:

dist[]

path[]

final[]

① dist[]

记录:

源点到每个顶点当前已知的最短距离

例如:

dist[3] = 7

表示目前知道:

源点 → 3 的最短距离是7。

② path[]

记录:

最短路径上,该顶点的前驱结点是谁

比如:

path[3] = 2

说明:

…… → 2 → 3

所以最后可以利用 path[] 把完整路径还原出来。

③ final[]

这个非常重要‼️

final[i] = true

表示:

顶点i的最短距离已经最终确定。

也就是说,后面不会再修改它。

🔥 Dijkstra到底在干什么?

其实就一句话:

每次从“还没确定最短路的顶点”中,挑一个 dist 最小的出来,把它确定下来,然后利用它去更新其他顶点。

这个过程叫:

「选最小 + 松弛」

Dijkstra步骤直接背👇

① 初始化

dist[i] = ∞

path[i] = -1

final[i] = false

源点:

dist[s] = 0

② 选择当前dist最小的顶点

从所有:

final[i] == false

的顶点中,

找到:

dist[i]

最小的那个。

记作:

u

然后:

final[u] = true

③ 用u更新其他顶点

遍历 u 的邻接点 v:

如果发现:

dist[u] + arc[u][v] < dist[v]

那么更新:

dist[v] = dist[u] + arc[u][v]

path[v] = 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更短。

于是更新:

dist[i][j] = 7

也就是:

dist[i][j]

=

min(dist[i][j],

    dist[i][k] + dist[k][j])

🔥 这就是Floyd最核心的公式!

📌 Floyd为什么是三个for循环?

因为它要依次考虑:

k = 0,1,2,...,n-1

作为中间点。

核心代码逻辑就是:

for k

    for i

        for j

            dist[i][j] =

            min(dist[i][j],

                dist[i][k] + dist[k][j])

所以时间复杂度:

⏱️ 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