对于邻接表来说,也有一个最适合的算法——Dijkstra算法。
Dijkstra算法是求解单源最短路径的算法。
什么是单源?举个例子,Floyd算法是多源的,有多个源点,能求解从任意点到任意点的距离。而Dijkstra算法是单源,的只能有一个源点,然后从这个源点(起点)出发,求解到达任意点的距离。
求解Dijkstra算法需要注意不能有负权边。
这里额外提一个负环的概念:负环并不意味着环中每条边都是负权,而是说,环的总权值为负。存在负环也就没有最短路可言。(一直绕就行)
下面举个简单的例子来说明Dijkstra算法:
步骤:
- 先从起始点1开始,影响他周围的三个点(更新最短路)。
- 更新后再从这三个点中,选取一个值最小的点固定住作为新的起始点 (没有比这个值更小的可能了),然后去影响它周围的点(更新最短路)。
- 如此循环下去。
代码实现:
- 每次如何选出值最小的点。——使用优先队列存储到达某个节点的最短路 (堆优化)
#include <iostream>
#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
using namespace std;
struct node{
int now,dis;//now:当前节点 ; dis:到这个点的最短路长
bool operator<(const node &b)const{ //STL的优先队列默认是大顶堆,重载小于号使它变成小顶堆。
return this->dis > b.dis;
}
};
struct edge{
int e,v; // e:这条边的重点; v这条边的权值
};
int n,m,s,ans[100005];
//n:点的数量; m:边的数量; s:起点的编号; ans[N]:存储每个点的最短路
int main(){
memset(ans,0x3F,sizeof(ans));//每个点的最短路需要被初始化成极大值
cin>>n>>m>>s;
vector<vector<edge> >edg(n+1,vector<edge>());//二维vector存储边的信息
for(int i=0;i<m;i++){
int a,b,c;
cin>>a>>b>>c; //输入每条边的起点、终点、权值
edg[a].push_back((edge){b,c});
edg[b].push_back((edge){a,c});//正反都存一遍
}
priority_queue<node> que;
ans[s]=0; //到起点的最短路置为0
que.push((node){s,0});//把起点压入队列
while(!que.empty()){
node temp=que.top();//每次拿出堆顶元素
que.pop();
if(temp.dis>ans[temp.now]){ //做一个剪枝,如果堆顶元素已经被固定过,就直接跳过
continue;
}
for(int i=0;i<edg[temp.now].size();i++){//遍历以它为起点的每条边
int e=edg[temp.now][i].e,v=edg[temp.now][i].v;
if(ans[e]>ans[temp.now]+v){ //更新相邻节点的最短路
ans[e]=ans[temp.now]+v;
que.push((node){e,ans[e]});//更新之后的节点压入队列
}
}
}
for(int i=1;i<=n;i++){
if(ans[i]==0x3F3F3F3F){//如果还是极大值,说明没有到这个点的最短路
cout<<-1<<endl;
}else{
cout<<ans[i]<<endl;//输出最短路
}
}
return 0;
}