dijkstra只能处理没有负权边的图,相较于spfa算法更适用于稠密图,因为spfa算法的时间复杂度最坏是O(nm)如果m=n2,那么时间复杂度就很差了,如果有些题特意卡你spfa,就必须用dijkstra或者堆优化的Dijkstra了。
dijkstra分为未堆优化的和堆优化的两种,未堆优化的时间复杂度是O(n2),堆优化的时间复杂度是O((n+m)logn)。
算法思想:
我们可以把整个图的所有点分成两个集合S,U。S代表的是已经求出的到顶点s(源点)的顶点集合。U表示还未求出的最短路径的顶点集合。
初始时,S中只有起点s,U中是除s之外的所有顶点,然后从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 然后,再从U中找出路径最短的顶点,并将其加入到S中;接着,更新U中的顶点和顶点对应的路径。 … 重复该操作,直到遍历完所有顶点。
实现方法:
1,初始时,S只包含起点s;U包含除s外的其他顶点,且U中顶点的距离为”起点s到该顶点的距离”[例如,U中顶点v的距离为(s,v)的长度,然后s和v不相邻,则v的距离为∞]。
2,从U中选出”距离最短的顶点k”,并将顶点k加入到S中;同时,从U中移除顶点k。
3,更新U中各个顶点到起点s的距离。之所以更新U中顶点的距离,是由于上一步中确定了k是求出最短路径的顶点,从而可以利用k来更新其它顶点的距离;例如,(s,v)的距离可能大于(s,k)+(k,v)的距离。
4,重复步骤(2)和(3),直到遍历完所有顶点。
图解:
以顶点D为起点,下图中的B(23 )应为B(13)。
未堆优化的Dijkstra:这里存图的方式是链式前向星,就是一种用数组模拟链表的过程。着重理解一下数组vis[],vis[i]=1表示节点I从上述的集合U转移到了集合S,也就是说明了节点I到原点的最短距离已经找到了。
#include<iostream>
#include<cstring>
#define maxn 10005
#define inf 2147483647
using namespace std;
int n,m,s;
int head[maxn],dis[maxn];
bool vis[maxn];
int t;
struct edge{
int from,to,w,next;
}e[500005];
void add(int u,int v,int w){
e[t].from =u;
e[t].to =v;
e[t].w =w;
e[t].next =head[u];
head[u]=t++;
}
void dijkstra(int s){
for(int i=1;i<=n;i++)dis[i]=inf;
dis[s]=0;
for(int i=head[s];i!=-1;i=e[i].next ){
int v=e[i].to ;
if(dis[v]>dis[s]+e[i].w )dis[v]=dis[s]+e[i].w ;
}
vis[s]=1;
for(int i=1;i<n;i++){
int minw=inf,id;
for(int j=1;j<=n;j++){
if(dis[j]<minw&&!vis[j]){
minw=dis[j];
id=j;
}
}
vis[id]=1;
for(int i=head[id];i!=-1;i=e[i].next ){
int v=e[i].to ;
if(dis[v]>dis[id]+e[i].w )
dis[v]=dis[id]+e[i].w ;
}
}
}
int main(){
memset(head,-1,sizeof(head));
cin>>n>>m>>s;
int u,v,w;
for(int i=1;i<=m;i++){
cin>>u>>v>>w;
add(u,v,w);
}
dijkstra(s);
for(int i=1;i<=n;i++)cout<<dis[i]<<" ";cout<<endl;
}
堆优化的dijkstra:
分析上面的代码,我们每次都要遍历所有节点来找到最新的距离源点最近的点,扫一遍是O(n),如果我们建立一个最小堆,把更新的点都丢到最小堆里面去,每次直接取出堆顶元素就是距离源点最近的节点了。
因为priority_queue默认是最大堆,所以我们需要重载一下符号’<'实现最小堆。一旦节点从最小堆里被抛了出来,也就是上面的从集合U转移到了集合S,即节点i到原点的最短距离已经找到了。
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#define maxn 100010
#define inf 1e8
using namespace std;
int n,m,s,t;
int head[maxn],dis[maxn];
bool vis[maxn];
struct edge{
int to,w,next;
}e[2*maxn];
int cnt;
void add(int u,int v,int w){
e[++cnt].to =v;
e[cnt].w =w;
e[cnt].next =head[u];
head[u]=cnt;
}
struct node{
int id,dis;
bool operator<(const node &a)const{
return dis>a.dis ;
}
};
void duijkstra(int s){
for(int i=1;i<maxn;i++)dis[i]=inf,vis[i]=0;
priority_queue<node>q;
dis[s]=0;
node temp;temp.id =s,temp.dis =dis[s];
q.push(temp);
while(q.size() ){
node temp=q.top() ;q.pop() ;
int u=temp.id ,w=temp.dis ;
if(vis[u])continue;
vis[u]=1;
for(int i=head[u];i;i=e[i].next ){
int v=e[i].to ;
if(dis[v]>w+e[i].w ){
dis[v]=w+e[i].w ;
node temp;temp.id =v,temp.dis =dis[v];
q.push(temp); //这里vis[v]一定是0,因为vis[]=1的节点已经是最短距离了,不可能被更新。
}
}
}
}
int main(){
cin>>n>>m>>s>>t;
int u,v,w;
for(int i=1;i<=m;i++){
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
duijkstra(s);
cout<<dis[t]<<endl;
return 0;
}