迪杰斯特拉算法是一种求解图的单点最短路径的算法。
一句话来说就是找最近点,更新相邻距离,再找最近点,更新相邻距离
迪杰斯特拉算法的原理是
1.首先在没有中间节点的情况下,也就是直达路径中找到到达某点p的最短路径。易知,该路径一定是原点到点p的最短路径。将点p标记在vis数组中,并将最短路径的值存在dist数组中。
2.再对所有节点进行松弛操作,也就是下一个节点的最短路径有两种情况,一种是经过某个已知的最短路(就是被vis标记的最短路径),第二种情况是直达。所以,求解下一个最短路径就是求解递推公式
dist[i]=min(dist[i] , map[i][now]+dist[now])
now是上一个最短路径。
3.找到最短的dist并标记在vis中,并迭代2,3步。
总体来说,迪杰斯特拉算法是按从小到大的顺序求解到各个点的最短路径,每求出一个最短路径,下次循环时就判断所有的点(其实等同于没有求出最短路径的点)如果经过上一个已经求出最短路径的点,是否会出现更短的路径(这一步就成为松弛操作)。在遍历所有点的同时,找到没有求出最短路径的点中的最短路径,并保存。
没有优化的迪杰斯特拉算法的时间复杂度为O(n*n)。
下面是一张图解:
以下是参考代码:
#include
using namespace std;
#define inf 0x7fffffff
long long int dist[100100];
int Map[1000][1000];
int vis[1010];
int main(){
int n,m,s;
while(~scanf("%d%d%d",&n,&m,&s)){
for( int i=1;i<=n;i++){
for( int j=1;j<=n;j++){
Map[i][j]=0x7fffffff;
}
dist[i]=0x7fffffff;
}
memset(vis,0,sizeof(vis));
for( int i=1;i<=m;i++){
int from,to,val;
cin>>from>>to>>val;
Map[from][to]=min(val,Map[from][to]);
Map[to][from]=min(val,Map[from][to]);
}
dist[s]=0;
vis[s]=1;
int start=s;
for( int j=1;j<=n;j++){
int next, minn=0x7fffffff;;
for( int i=1;i<=n;i++){
if(Map[start][i]!=inf) {
dist[i]=min(dist[i],Map[start][i]+dist[start]);
}
if(vis[i]==0&&dist[i]<minn){
next=i;
minn=dist[i];
}
}
if(minn==inf)break;
start=next;
vis[start]=0;
}
for( int i=1;i<=n;i++){
cout<<dist[i];
if(i!=n) cout<<" ";
}
cout<<endl;
}
}
对迪杰斯特拉算法的优化:
上面的迪杰斯特拉算法主要缺陷是,每当找到一个最短路径,如果需要找下一个最短路径,就需要在完成松弛操作之后,遍历dist数组,寻找其中的最小值。遍历dist数组的时间复杂度为O(n)。
如果图的边数为n*(n-1),那么每找到一个最小值,所要进行的松弛操作数就是n-1,这和遍历dist数组可以同时进行,算法优化的空间不大。
然而,如果是稀疏图,每找到一个最小值,所要进行的松弛操作数就远小于n-1,这时就可以对算法进行优化。优化的关键是省去对dist的线性查找,如果每次可以直接返回dist中的最大值,就可以大大减小算法的时间复杂度。
堆优化后的迪杰斯特拉算法复杂度为ElogE
#include
using namespace std;
#define inf 0x7fffffff
struct p{
long long int to,val,next;
};
p edge[200100];
long long int dist[100100];
int vis[100100];
int head[100100];
int cnt;
void add_edge( long long from,long long to,long long val){
edge[++cnt].val=val;
edge[cnt].to=to;
edge[cnt].next=head[from];
head[from]=cnt;
}
struct node{
long long int from,val;
bool operator <(const node &a ) const{
return a.val<val;
}
};
int main(){
int n,m,s;
while(~scanf("%d%d%d",&n,&m,&s)){
cnt=0;
memset(edge,0,sizeof(edge));
for( int i=1;i<=n;i++){
dist[i]=1e12;
head[i]=0;
vis[i]=0;
}
for( int i=1;i<=m;i++) edge[i].val=1e12;
for( int i=1;i<=m;i++){
long long int from,to,val;
scanf("%lld%lld%lld",&from,&to,&val);
add_edge(from,to,val);
}
dist[s]=0;
priority_queue<node>q;
q.push({s,0});
while(!q.empty()){
node now=q.top();
q.pop();
if(vis[now.from]) continue;
vis[now.from]=1;
for( long long int i=head[now.from];i!=0;i=edge[i].next){
int j=edge[i].to;
if(dist[now.from]+edge[i].val<dist[j]){//进行松弛操作
dist[j]=dist[now.from]+edge[i].val;
q.push({j,dist[j]});
}
}
}
for( int i=1;i<=n;i++){
printf("%lld ",dist[i]);
}
cout<<endl;
}
}
迪杰斯特拉算法的优化可以总结为以下几步:
1.将最短距离出队。
2.进行松弛操作,并将成功松弛的点入队。