Dijkstra算法

思路

定义数组/变量


  • 定义一个 $dis$ 数组。 $dis_i$ 表示第 $i$ 个点目前距离源点的最短距离。
  • 定义一个 $V$ 数组,保存已经找到了最短路径的顶点的集合。

初始化


  • 把 $dis$ 数组全部初始化为无穷大。$dis_s$(源点)设为 $0$。

具体实现步骤


  • 每次从 $dis$ 数组里找出最小的数,同时它没有在 $V$ 数组里(目前离源点最近的点)。
  • 把刚才找到的那个点加入 $V$ 数组里。
  • 寻找和这个点联通的其他点,更新与它联通的点的距离。
  • 一直循环以上步骤,直到数组 $V$ 满了为止(也就是说所有的点都已成为最短路径)。

一些疑问

Q : 如何保证每次从 $dis$ 数组里找出的最小值,那个点一定是最短距离,会不会存在从其他点再辗转到这个点还比当前距离小的呢?

A : 因为这个点目前未锁定且离源点最近,所以它不可能通过未锁定最短路的点来更新,因为其它未锁定的点离源点的距离都比这个点远;它也不可能被已锁定的点更新,因为已经被已锁定的点更新过了。所以,可以确定这个点就是离源点最短。

关于邻接表:(挂几个图就明白了)

(图来自这篇 ​​blog​​)


【学习笔记】单源最短路径_i++

其实就是 $mapn_i$ 后面都存的是和它联通的点的信息。 (代码里用邻接表时没有用指针)

代码

(不加优化)邻接表存图

#include<iostream>
#include<cstdio>
#include<vector>
#include<queue>
using namespace std;

struct node
{
int x;
long long num;
};

const long long inf = 2147483647;
long long n,m,s;
long long dis[10005];//存储每个点到源点的最短路程
vector<node> mapn[10005];
int V[10005];
int main(){

cin >> n >> m >> s;
for(int i=1; i<=m; i++){
int x,y;
long long num;
cin >> x >> y >> num;
mapn[x].push_back((node){y,num});
}

for(int i=1; i<=n; i++)dis[i] = inf;//初始化
dis[s] = 0;
int cnt=n;
while(cnt){
long long minn=inf,pos;//存储当前dis里最小值,位置
//找dis里的最小值
for(int i=1; i<=n; i++){
if(dis[i] < minn && V[i] != 1){
minn = dis[i];
pos = i;
}
}

cnt--;
V[pos] = 1;

//更新和pos相邻的点的距离
for(int i=0; i<mapn[pos].size(); i++){
int npos=mapn[pos][i].x;
//if(mapn[pos][i] >= 1 && V[i] != 1)
dis[npos] = min(dis[npos],mapn[pos][i].num+minn);
}
}
for(int i=1; i<=n; i++)
cout << dis[i] << ' ';
return 0;
}

优先队列优化后

#include<iostream>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
#include<vector>
using namespace std;

long long const inf = 2147483647;
struct node{
int x;
int num;
bool operator<(const node b)const // 注意 const
{
return num>b.num; // 按 num 从小到大,注意符号
}
};
int n,m,s;// n 个点,m 条有向边,出发点 s
int dis[100008];
vector<node> mapn[100009];
int v[100009];
int main(){
priority_queue<node> q;

cin >> n >> m >> s;
for(int i=1; i<=m; i++){
int x,y,z;
cin >> x >> y >> z;
mapn[x].push_back((node){y,z});
}

for(int i=1; i<=n; i++) dis[i] = inf;

dis[s] = 0;
q.push((node){s,0}) ;
while(!q.empty()){
node nodet = q.top() ;//目前离源点最近的点
int pos = nodet.x ;//点的编号

q.pop() ;
if(v[pos] == 1) continue;
v[pos] = 1;
for(int i=0; i<mapn[pos].size(); i++){
int newnode = mapn[pos][i].x;//要松弛的点的编号
int len1 = mapn[pos][i].num;//目前离源点最近的点距要松弛的点的距离
// cout << newnode << ' ' << len1 << endl;
if(len1+nodet.num < dis[newnode]){
dis[newnode] = len1+nodet.num ;
q.push((node){newnode,len1+nodet.num});

}
}
}
for(int i=1; i<=n; i++)cout << dis[i] << ' ';
return 0;
}