Dijkstra算法

758 字
4 分钟
Dijkstra算法

Dijkstra 算法#

Dijkstra 算法用来求“非负权图”中的单源最短路。

单源最短路的意思是:从一个起点出发,到其他点的最短距离。

这里整理的是堆优化版本,适合边数比较多、点数也比较大的情况。

1、适用条件#

Dijkstra 有一个前提:

边权不能是负数

如果图中有负权边,就不能直接用 Dijkstra。

常见场景:

给 n 个点、m 条有向边,求 1 号点到 n 号点的最短距离。

2、核心思想#

维护一个距离数组:

dist[i]

表示从起点到 i 的当前最短距离。

每次从还没有确定最短路的点中,取出距离最小的点 ver。因为所有边权非负,所以这个点的最短距离已经确定。

然后用它去更新邻点:

如果 dist[j] > dist[ver] + w
就更新 dist[j]

这个过程叫“松弛”。

3、为什么用小根堆#

普通 Dijkstra 每次要扫描所有点找最小距离,复杂度高。

堆优化版本用优先队列维护:

{距离, 点编号}

每次直接取出当前距离最小的点。

4、链式前向星存图#

int h[N], e[M], w[M], ne[M], idx;

含义:

h[a]:点 a 的第一条边
e[i]:第 i 条边的终点
w[i]:第 i 条边的权重
ne[i]:同起点的下一条边

加边:

void add(int a, int b, int c) {
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx++;
}

5、C++ 模板#

#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int N = 150010;
const int M = 150010;
const int INF = 0x3f3f3f3f;
int n, m;
int h[N], e[M], w[M], ne[M], idx;
int dista[N];
bool st[N];
void add(int a, int b, int c) {
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx++;
}
int dijkstra() {
memset(dista, 0x3f, sizeof dista);
priority_queue<PII, vector<PII>, greater<PII>> heap;
dista[1] = 0;
heap.push({0, 1});
while (!heap.empty()) {
auto t = heap.top();
heap.pop();
int distance = t.first;
int ver = t.second;
if (st[ver]) continue;
st[ver] = true;
for (int i = h[ver]; i != -1; i = ne[i]) {
int j = e[i];
if (dista[j] > distance + w[i]) {
dista[j] = distance + w[i];
heap.push({dista[j], j});
}
}
}
if (dista[n] == INF) return -1;
return dista[n];
}
int main() {
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
while (m--) {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
add(a, b, c);
}
printf("%d\n", dijkstra());
return 0;
}

6、st 数组的作用#

bool st[N];

st[ver] = true 表示 ver 的最短距离已经确定。

堆里可能会存在同一个点的多个距离版本。比如先入堆一个较大的距离,后来又找到更短距离。旧版本不会马上消失,所以取出来时要判断:

if (st[ver]) continue;

7、容易写错的地方#

(1)不要在 #define 后面加分号#

如果写:

#define x first;

会导致展开后多出分号。更推荐直接用:

t.first
t.second

(2)初始化邻接表#

memset(h, -1, sizeof h);

没有初始化,遍历边时会出错。

(3)Dijkstra 不能处理负权边#

只要出现负权边,就要考虑 Bellman-Ford、SPFA 或其他算法。

总结#

Dijkstra 可以记成:

小根堆取最近点 -> 标记最短路已确定 -> 用它松弛所有出边

它适合非负权图,是图论最短路里非常常用的模板。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Dijkstra算法
https://blog.tjcheng.cn/posts/migrated/old-92/
作者
TengJinCheng
发布于
2026-03-10
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
TengJinCheng
Hello, I'm Teng.
公告
欢迎来到TengJinCheng工作铺!
音乐
封面

音乐

暂未播放

0:000:00
暂无歌词
分类
标签
站点统计
文章
157
分类
7
标签
114
总字数
109,939
运行时长
0 天
最后活动
0 天前
站点信息
构建平台
Local
博客版本
Firefly v6.13.4
文章许可
CC BY-NC-SA 4.0

文章目录