简单介绍
迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959
年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。
问题的提出:给定一个带权无向图 G 和源点 v0,求 v0 到 G 中其他每个顶点的最短路径。限定各边上的权值非负
算法分析&思想讲解:
从起点到一个点的最短路径一定会经过至少一个“中转点”
(例如下图1到5的最短路径,中转点是2。特殊地,我们认为起点1也是一个“中转点”)。
显而易见,如果我们想求出起点到一个点的最短路径,那我们必然要先求出中转点的最短路径(例如我们必须先求出点2 的最短路径后,才能求出从起点到5的最短路径)。
换句话说,如果起点1到某一点V0的最短路径要经过中转点Vi,那么中转点Vi一定是先于V0被确定了最短路径的点。
为求得这些最短路径,Dijkstra 提出按路径长度的递增次序,逐步产生最短路径的算法。
我们把点分为两类,一类是已确定最短路径的点,称为“白点”,另一类是未确定最短路径的点,称为“蓝点”。如果我们要求出一个点的最短路径,就是把这个点由蓝点变为白点。从起点到蓝点的最短路径上的中转点在这个时刻只能是白点。
Dijkstra的算法思想,就是一开始将起点到起点的距离标记为0,而后进行n次循环,每次找出一个到起点距离dis[u]最短的点u,将它从蓝点变为白点。随后枚举所有的蓝点vi,如果以此白点为中转到达蓝点vi的路径dis[u]+w[u][vi]更短的话,这将它作为vi的“更短路径”dis[vi](此时还不确定是不是vi的最短路径)。
就这样,我们每找到一个白点,就尝试着用它修改其他所有的蓝点。中转点先于终点变成白点,故每一个终点一定能够被它的最后一个中转点所修改,而求得最短路径。
以这张图为例
首先求出长度最短的一条最短路径:0 -> 2,
长度为 5(2 号点).顶点 2 的最短路求出来之后,
0 到其他顶点的最短路径长度有可能要改变,比如 0 -> 1 从原来的 ∞ 变成了 20, 0 -> 5 从原来的 ∞ 变成了 12.
这样次短的最短路就是 12(5 号点).
5 的最短路求出后,0 到其他点的最短路还会发生改变。比如 0 -> 3 从原来的 30 变为了 22,0 -> 4 也从 ∞ 变为了 28。这时第三短的最短路就确定了,是 20(1 号点).
之后再依次确定第四短的最短路是 22(3 号点).
第五短的最短路是 28 (4 号点)
普通模版
时间复杂度O(n2)
void Dijkstra()
{
memset(dis,127,sizeof(dis));
memset(exist,0,sizeof(exist));
int x,y,z;
int i,j;
int cmin;
dis[1]=0;
for(j=1;j<=n;j++) {
cmin=INF;
for(i=1;i<=n;i++) {
if(exist[i]) continue; //不考虑已经选的点
if(dis[i]<cmin) { cmin=dis[i]; x=i; } //选出dis最小的点
}
exist[x]=true;//选中
for(i=one[x];i;i=Next[i]) {//scan all the edges;
y=ver[i]; z=edge[i];
if(dis[y]>dis[x]+z) //improve node y;
dis[y]=dis[x]+z;
}
}
return;
}
堆优化模板
时间复杂度O((n+m)logn)
核心代码
priority_queue< pair<int,int> > q;
//大根堆(优先队列),pair的第二维为节点编号;
//pair的第一维为dis的相反数,利用相反数变成小根堆;
//默认对pair的第一维排序;
void Dijkstra()
{
memset(dis,127,sizeof(dis));
memset(exist,0,sizeof(exist));
while(q.size()) q.pop();
int u,x,y,z;
dis[1]=0;
q.push(make_pair(0,1));
while(!q.empty()) {
x=q.top().second; q.pop();
if(exist[x]) continue;//已经被选中
exist[x]=true;//选中
for(int i=one[x];i;i=Next[i]) {//scan all the edges;
y=ver[i]; z=edge[i];
if(dis[y]>dis[x]+z) { //relax node y;
dis[y]=dis[x]+z;
q.push(make_pair(-dis[y],y)); //push node y;
}
}
}
return;
}
完整代码
#include<bits/stdc++.h>
using namespace std;
priority_queue< pair<int,int> > q;
//大根堆(优先队列),pair的第二维为节点编号;
//pair的第一维为dis的相反数,利用相反数变成小根堆;
//默认对pair的第一维排序;
const int MAXN=10000*2+5;
const int MAXM=10000+5;
int dis[MAXN];
bool exist[MAXN];
int one[MAXN],ver[MAXN],edge[MAXN],Next[MAXN],adj[MAXN];
int n,m;
int tot=0;
/*输入n, m,表示 n个城市和 m条路;
接下来m行,每行a b c,表示城市a与城市b有长度为c的路。*/
void add(int a,int b,int c)//from a to b;
{
tot++;
if(one[a]==0) one[a]=tot;
Next[adj[a]]=tot; adj[a]=tot;
edge[tot]=c;
ver[tot]=b;
return;
}
void Init()
{
int i,j;
int a,b,c;
cin>>n>>m;
for(i=1;i<=m;i++) {
scanf("%d%d%d",&a,&b,&c);
add(a,b,c); //把一条无向边当两条有向边存
add(b,a,c);
}
return;
}
void Dijkstra()
{
memset(dis,127,sizeof(dis));
memset(exist,0,sizeof(exist));
while(q.size()) q.pop();
int u,x,y,z;
dis[1]=0;
q.push(make_pair(0,1));
while(!q.empty()) {
x=q.top().second; q.pop();
if(exist[x]) continue;//已经被选中
exist[x]=true;//选中
for(int i=one[x];i;i=Next[i]) {//scan all the edges;
y=ver[i]; z=edge[i];
if(dis[y]>dis[x]+z) { //relax node y;
dis[y]=dis[x]+z;
q.push(make_pair(-dis[y],y)); //push node y;
}
}
}
return;
}
int main()
{
// freopen("1.in","r",stdin);
Init();
Dijkstra();
if(dis[n]!=dis[0]) cout<<dis[n];
else cout<<"-1";
return 0;
}