dijkstra是单源最短路径算法,要两个数组来维护需要的信息
dis[i]用来维护起始点到i点的最短距离,初始化为一个很大的数字 0x3f3f3f3f
vis[i]用来维护是否访问过该点,初始化为0,即没有访问过
dijkstra算法有朴素版实现和优先队列优化版本
朴素版
dijkstra算法思路如下:
1、确定一个起始点begin
2、让dis[begin] = 0
3、选取一个让dis[s]最小,且没有访问过的点s(第一次访问的节点是起始点)
4、让vis[s] = 1表示已经访问过s点
5、对于与s相连的点i,比较dis[s]+s、i之间的距离和dis[i]的大小,
如果前者比较小,说明找到一条从起始点到i点更短的路,就要更新dis[i]
让dis[i] = dis[s] + s、i之间的距离。
如果前者比较大,说明没有找到一条从起始点到i点更短的路,不需要更新dis[i]
6、重复3到5的步骤直到所有节点都被访问过
7、dis数组更新完毕,当前dis[i]就表示起始点到i点的最短距离
优先队列优化版
优化的是步骤3,从原本的遍历所有点比较大小再选取最小的点,
变成了直接选优先队列的头,时间复杂度从O(n)变为O(logn)
朴素版的时间复杂度为O(n²) 优化版的时间复杂度为O(nlogn)
来一个例子
A、B、C、D、E、F点的编号分别为1、2、3、4、5 、6
假如要计算1号点到其他点的最短距离
1.就把1号点当作起点
2.dis[1] = 0,dis[2] = 0x3f3f3f3f,dis[3] = 0x3f3f3f3f,dis[4] = 0x3f3f3f3f,dis[5] = 0x3f3f3f3f,dis[6] = 0x3f3f3f3f
3.因为dis[1]为0所以这时候dis[1]最小,所以选1号点,vis[1] = 1表示访问过了
4.1号点选完之后,与1号点相连的点有2号 5号 6号 点
5.由于dis[2] > dis[1] + 6 所以更新dis[2] dis[2] = dis[1] + 6
由于dis[6] > dis[6] + 1 所以更新dis[6] dis[6] = dis[1] + 1
由于dis[5] > dis[1] + 5 所以更新dis[5] dis[5] = dis[1] + 5
现在dis[1] = 0, dis[2] = 6, dis[3] = 0x3f3f3f3f, dis[4] = 0x3f3f3f3f, dis[5] = 5,dis[6] = 1
6.继续选下一个点,因为1号点选过了,所以应该选除了1号点之外dis最小的点,即6号点
7.与6号点相连的有1号 2号 3号 4号 5号
由于dis[2] > dis[6] + 2 所以更新dis[2] dis[2] = dis[6] + 2
由于dis[3] > dis[6] + 8 所以更新dis[3] dis[3] = dis[6] + 8
由于dis[4] > dis[6] + 4 所以更新dis[4] dis[4] = dis[6] + 4
由于dis[5] < dis[6] + 9 所以不更新
现在dis[1] = 0,dis[2] = 3, dis[3] = 9,dis[4] = 5,dis[5] = 5,dis[6] = 1
继续选没有选过的点直到所有的点都被选过
洛谷P3371
代码
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100010;
int n,m;
struct edge{
int v,w;
};
int dis[MAXN],vis[MAXN];
void dijkstra (int begin, vector<edge> v[]){
memset(dis,0x3f3f3f3f,sizeof(dis));
dis[begin] = 0;
for(int i = 1;i <= n; i++){
int s = 0,min = 0x3f3f3f3f;
vis[s] = 1;
for(int j = 1;j <= n; j++){
if(!vis[j] && dis[j] < min){
min = dis[j];
s = j;
}
}
vis[s] = 1;
for(auto x : v[s]){
if(dis[x.v] > dis[s] + x.w)
dis[x.v] = dis[s] + x.w;
}
}
};
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int s,t1,t2,w;
cin >> n >> m >> s;
vector<edge> v[n+1];
for(int i = 1;i <= m; i++){
cin >> t1 >> t2 >> w;
v[t1].push_back({t2,w});
}
dijkstra(s, v);
for(int i = 1;i <= n; i++){
if(dis[i] < 0x3f3f3f3f)
cout << dis[i] << " ";
else cout << 2147483647 << " ";
}
}
优先队列优化版
洛谷P4779
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100010;
int n,m;
struct edge{
int v,w;
};
int dis[MAXN],vis[MAXN];
struct info{
int d,v;
bool operator < (const info & a) const {
return d > a.d;
}
};
int main()
{
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int s,t1,t2,w;
cin >> n >> m >> s;
vector<edge> v[n+1];
for(int i = 1;i <= m; i++){
cin >> t1 >> t2 >> w;
v[t1].push_back({t2,w});
}
function<void(int)> dijkstra = [&](int begin){
memset(dis,0x3f3f3f3f,sizeof(dis));
dis[begin] = 0;
priority_queue< info > pq;
pq.push({0,begin});
while(!pq.empty()){
int vi = pq.top().v;
pq.pop();
if(vis[vi]) continue;
vis[vi] = 1;
for(auto x : v[vi]){
if(dis[x.v] > dis[vi] + x.w) {
dis[x.v] = dis[vi] + x.w;
pq.push({dis[x.v],x.v});
}
}
}
};
dijkstra(s);
for(int i = 1;i <= n; i++){
cout << dis[i] << " ";
}
}