dijkstra------单源最短路径
该算法用来解决带权图的单源最短路径问题,也就是说求起点带每个点的最短路程,这是一个贪心算法,同样这给我们这些小菜鸡带来了巨大震撼,太难了!!!要知后事如何,且听我徐徐道来~
我们想要算一个起点到别的所有点的最短路径,我们应该怎样来实现呢?首先想到暴力,但是一旦顶点多一点,路径的数量直接爆炸。然后我们就想Floyd可不可以,但是我们不要忘了Floyd适合求每一对点的最短路,而你只需要从1开始,这样未免太浪费了吧。SPFA呢?它是可以处理负边,但是速度要比Dijkstra慢许多。这时我们就要用Dijkstra啦~
我们首先挑一个没被标记的且目前距离最小的u,这个是我们从起点到u的最短距离,然后标记以便以后不再被更新,接下来就开始独属于我们Dijkstra的松弛了。用u来更新他的相邻点的距离,更新从起点到所在的点的最小值。

| 第一次 | 第二次 | 第三次 | 第四次 | 第五次 | |
|---|---|---|---|---|---|
| b | 2 (a,b) | ||||
| c | 5(a,c) | 3 (a,b,c) | |||
| d | ∞\infty∞ | 5(a,b,d) | 5(a,b,d) | 5 (a,b,d) | |
| e | ∞\infty∞ | ∞\infty∞ | 7(a,b,c,e) | 7(a,b,c,e) | 6 (a,b,c,e) |
| f | ∞\infty∞ | ∞\infty∞ | 4 (a,b,c,f) | ||
| 终点集 | {a,b} | {a,b,c} | {a,b,c,f} | {a,b,c,f,d} | {a,b,c,f,d,e} |
那么接下来我们就来实现一下代码:
例子
3 3
1 2 2
1 3 5
2 3 1
0 2 3
7 10
1 2 5
1 3 2
2 4 1
3 4 6
3 6 8
2 5 6
4 6 2
4 5 1
5 7 7
6 7 3
0 5 2 6 7 8 11
朴素版:
#include<bits/stdc++.h>
using namespace std;
const int N=1005,INF=0x3f3f3f3f;
int g[N][N];//邻接矩阵,代表u直达v的边权
int dis[N];//起点到x当前最短距离
bool vis[N];//这个点有没有被标记
void dijkstra(int n,int s)
{
memset(dis,0x3f,sizeof(dis));//初始化为无穷大
memset(vis,false,sizeof(vis));//初始化为false
dis[s]=0;//起点到自身为0
for(int i=0;i<n;i++)//遍历每个数,每遍历一个确定一个点的最短路径
{
int u=-1,min_d=INF;//初始化
for(int j=1;j<=n;j++)//遍历全部顶点,选一个还没确定最短路,距离值最小的顶点u
{
if(!vis[j]&&dis[j]<min_d)//若没有标记并且小于当前最小,更新
{
min_d=dis[j];
u=j;
}
}
if(u==-1) break;//若没有循环结束
vis[u]=true;//标记
//松弛操作
for(int v=1;v<=n;v++)//从起点到u再到v的路径,对比v当前路径,更新最小值
{
if(!vis[v]&&g[u][v]!=INF)
dis[v]=min(dis[v],dis[u]+g[u][v]);
}
}
}
signed main()
{
int n,m;
cin>>n>>m;
memset(g,0x3f,sizeof(g));//初始值为无穷大
for(int i=1;i<=n;i++) g[i][i]=0;//每个点到自身距离都为0
for(int i=0;i<m;i++)
{
int a,b,w;
cin>>a>>b>>w;
g[a][b]=min(g[a][b],w);
g[b][a]=min(g[b][a],w);//无向边
}
dijkstra(n,1);//从1开始
for(int i=1;i<=n;i++)
{
if(dis[i]==INF) cout<<"-1"<<" ";//若走不到输出-1
else cout<<dis[i]<<" ";//若能走到,输出最短路径
}
cout<<endl;
return 0;
}
相信聪明的你已经看出来了,这是朴素版的。那有没有更高级的呢?当然有啦~~ 接下来我们就来讲讲优化的版本------堆优化。
之前那个我们要每一轮遍历去找最小距离的点,而现在我们只用优先队列直接取出距离最小的点,那么复杂度会大大降低哦~
之前那个理解了,这个自然也就好理解多了。我们用小根堆来选出最小路径,再在此基础上进行松弛处理
#include<bits/stdc++.h>
using namespace std;
#define pii pair<int,int>
const int N=101,INF=0x3f3f3f3f;
vector<pii> adj[N];
int dis[N];
bool vis[N];
void dijkstra(int n,int s)
{
memset(dis,0x3f,sizeof(dis));
memset(vis,false,sizeof(vis));
dis[s]=0;
priority_queue<pii,vector<pii>,greater<pii>> q;//小根堆
q.push({0,s});//原点存入
while(!q.empty())//若不为空
{
auto ars=q.top();
q.pop();//最小值出栈
int d=ars.first;
int u=ars.second;
if(vis[u]) continue;//若被标记,则说明有最小值,这个值可以舍去了continue
vis[u]=true;//未被标记,说明当前是最短距离,标记
for(auto ans:adj[u])//我们来松弛一下相邻点
{
int v=ans.first;
int w=ans.second;
if(dis[v]>dis[u]+w)//若之前的最短距离>当前的,更新值
dis[v]=dis[u]+w;
q.push({dis[v],v});//入栈,这里旧的长的不用出栈,下一个循环if(vis[u]) continue;会判断
}
}
}
signed main()
{
int n,m;
cin>>n>>m;
for(int i=0;i<m;i++)
{
int a,b,w;
cin>>a>>b>>w;
adj[a].emplace_back(b,w);//这里用g[][]容易超限,所以用vector来存
adj[b].emplace_back(a,w);
}
dijkstra(n,1);
for(int i=1;i<=n;i++)
{
if(dis[i]==INF) cout<<"-1"<<" ";
else cout<<dis[i]<<" ";
}
return 0;
}
好了,这就是dijkstra的基础原理及代码,希望对你有所帮助>^<~~