再述 Dijkstra
再述 Dijkstra
学 Dijkstra 好久了,今天再学了一遍,感觉推翻了好多自己的知识……
定义
一种用于求非负权值的图的单源最短路径的算法。
方法
已知:如果要求从起始点 s 到某一个点 x 的最短路径,显然只能从某一个已确认为最短路径的点转移。
给个图:
假设我们的起始点是点 1,现在我们用数组记录从原点到所有点的最短路径:
1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|
0 |
由于其他点的最短路未知,故先用 代替,代码中用很大的一个数字代替即可。
注意到,我们由于要求出某个点出发,所有点的最短路,显然需要更新 次,其中 为顶点数量。
在这 次循环中,我们可以处理出由若干顶点组成的已知最短路集合,称之为 。
在每次循环中,用 可以找到距离 最近的那个点,更新其最短路表格,并将其加入 中。
最后得到的结果:
1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|
0 | 5 | 6 | 7 | 6 |
证明
如何证明这种算法是对的?
假设我们有一张图:
从 出发,求到 的最短路径。其中 这条路径已确认最短。
显然 这条路径并不会比 更优,且 这条边的权值一定非负(前提),所以 这条路径一定是最优的。
算算时间复杂度,两层 的循环,就 ,对于小数据可过。可允许大小约在 。
优先队列优化
想想能否优化时间复杂度?
注意到,由于是要求 个点的最短路,那么第一层的循环显然不能舍弃。
考虑优化时找到最近点的枚举步骤。
可以用一个优先队列存起来。存的东西:pair
类型,第一个元素是目前的最短路距离,第二个是点的编号。
那么众所周知,优先队列查找一个元素的时间复杂度是 的,其中 为元素个数。
每次查找都是一个 , 次外循环,每次还要通过 的时间复杂度更新最短距离。
所以时间复杂度即为 。
一般来说,只要图是联通的, 基本都会比 大,可近似为 。
局限性
但是,考虑到一种特殊的情况:完全图。
众所周知,完全图是一种 的特殊图,那么优先队列优化的时间复杂度就反而退化成了 ,反而不如朴素版。
代码
放下优先队列优化后的代码:
#include<bits/stdc++.h> using namespace std; const int MAXM=5e5+5; const int MAXN=1e4+5; int n,m,s; bool book[MAXN]; int dis[MAXN]; struct EDGE{ int to,w,pre; }edge[MAXM]; int head[MAXN]; priority_queue<pair<int,int>,vector<pair<int,int> > ,greater<pair<int,int> > > heap; void init() { for(int i=1;i<=n;i++) { dis[i]=INT_MAX; } return; } void add(int from,int to,int w,int num) { edge[num].to=to; edge[num].w=w; edge[num].pre=head[from]; head[from]=num; return; } int u,v,w; int main(){ scanf("%d%d%d",&n,&m,&s); init(); dis[s]=0; for(int i=1;i<=m;i++) { scanf("%d%d%d",&u,&v,&w); add(u,v,w,i); } heap.push(make_pair(0,s)); while(!heap.empty()) { int t=heap.top().second; heap.pop(); if(book[t]==true) { continue; } book[t]=true; for(int i=head[t];i!=0;i=edge[i].pre) { dis[edge[i].to]=min(dis[edge[i].to],dis[t]+edge[i].w); heap.push(make_pair(dis[edge[i].to],edge[i].to)); } } for(int i=1;i<=n;i++) { printf("%d ",dis[i]); } puts(""); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)