Dijkstra算法详解:
在解决单源点最短路径的问题时,常常用到经典的Dijkstra算法,其算法的本质思想是: 按路径长度递增依次产生最短路径。
下面给出算法的大致流程:
1.初始化所有结点并将起始点设为标记,进入以下循环
2.在到达某点的最短路径中找最小且未标记的点(可以用一维数组表示)
如:
数组下标:0 1 2 3 4 5
Len :- 0 5 10 2 -
这个数组表示 1号节点为初始节点,1号节点到达2号节点的最短路径为5,到3号为10,无法到达5号(具体可以以较大的数表示其路径)。
从中找到一个未标记且Len最短的一个,未标记用另一数组记录
如:
数组下标:0 1 2 3 4 5
标记 :0 1 0 0 1 0
此数组表示从初始节点到达4号节点的最短路径已找到
从以上两个数组中可以得出:此次循环找到的点为2号节点,进入下一步
3.标记找到的点,以此标记点为中间点重新计算所有未标记点的最短路径(更新最短路径表)
4.循环1.2步至n-1次(n为顶点数,若最后还有未被标记的,说明无法到达此点)
下面是核心代码:
while(count<n) { tempmin=INFINITE; for(i=1;i<=n;i++) { if(in[i]==0&&Len[i]<tempmin) //find the smallest one { tempmin=Len[i]; si=i; } } in[si]=1; count++; for(i=1;i<=n;i++) //updata the length { if(in[i]==0&&(tempmin+mGraph.matrix[si][i])<Len[i]) { Len[i]=tempmin+mGraph.matrix[si][i]; } } }
核心部分是上面的两个for循环,第一个for循环遍历所有结点,找到未标记并且长度最短的路径;第二个for循环也是遍历所有结点以上面找到的最短路径结点为中间点更新最短路径表;注意在第一个for循环之后要标记找到的点(说明到达此点的最短路径已找到)
下面用一个实例来具体说明:
若有这样一张有向图(此图为《数据结构》严蔚敏 p188页,书中没有详细讲解,现以此为实例)
V0-V5 共6个节点,节点间路径已标出,现要求从V0到其余各节点的最短路径;
有上面的算法流程可知,在使用Dijkstra算法时需要几个结构来保存我们想要的信息:
1.保存这张图的结构体
2.记录V0到其余各节点最短路径的数组(这里设为Len[n+1])
3.记录某节点是否已找到最短路径的数组(这里设为in[n+1])
接下来就是算法实现部分:
1.标记V0 -- in[1]=1 初始化 Len[]={INFINITE , 0 , INFINITE , 10, INFINITE , 30 , 100} 这里数组首元素未用到,数组下标从1开始表示V0以此类推
2.第一次循环与V0相邻的有V2、V4、V5,其中V2距离最短为10,标记V2,并且以V2为中间点(路径为V0->V2->Vx)更新最短路表,此时V3被更新为10+50=60,。
3.进入第二次循环,此时未被标记的有V1、V3、V4、V5,,其中从V0到这些的临时最短路分别为INFINITE、60、30、100,从中找到最小的即V4,将V4标记为1,以V4为中间点(路径为V0->V4->Vx)更新最短路表,此时V3被更新为50,V5被更新为90。
4.进入第三次循环,此时未被标记的有V1、V3、V5,其中临时最短路分别为INFINITE、50、90,从中找到最小的即V3,将V3标记为1,以V3为中间点(路径为V0->V4->V3->Vx)更新最短路表,此时V5被更新成60。
5.进入第四次循环,此时未被标记的有V1、V5,其中临时最短路分别为INFINITE、60,找到V5,标记为1,以V5为中间点更新最短路表,此时没有元素被更新
6.进入第五次循环,这次循环没找到任何东西
7.退出循环,Len表中即为V0到其余各个节点的最短路径。
以上就是Dijkstra算法最基本的思想,当然,在找最短路时我们常常会需要求出到达某点的最短路径,那么下面,我们就来看看要怎样记录下到达某点的最短路径:
在Dijkstra算法的前提下加入查询最短路径其实很简单,只要在每次更新最短路时保存在该顶点的父节点序号即可,最后输出时回退中间节点然后用堆栈输出即可
最后直接给出完整代码(写的很一般,还望指教):
#include <stdio.h> #include <string.h> #define MAX_LEN 100 #define INFINITE 1000 typedef struct graph { int nodenum; int edgenum; int matrix[MAX_LEN][MAX_LEN]; }Graph; typedef struct stack { int bottom; int top; int printout[MAX_LEN]; }mstack; int in[MAX_LEN]; int Len[MAX_LEN]; int path[MAX_LEN]; void InitStack(mstack *s) { s->bottom=0; s->top=0; memset(s->printout,0,sizeof(int)*MAX_LEN); } void push(mstack *s,int m) { s->printout[s->top++]=m; } int pop(mstack *s) { return s->printout[--s->top]; } void InitGraph(Graph *g,int n) { int i,j; for(i=1;i<=n;i++) for(j=1;j<=n;j++) { if(i==j)g->matrix[i][j]=0; else g->matrix[i][j]=INFINITE; } for(i=1;i<=n;i++) { in[i]=0; Len[i]=INFINITE; path[i]=0; } } int main() { int n,m,i,A,B,templen,count,min,tempmin,si,temp; while(scanf("%d %d",&n,&m)) { count=0; Graph mGraph; mGraph.edgenum=m; mGraph.nodenum=n; InitGraph(&mGraph,n); for(i=0;i<m;i++) { scanf("%d %d %d",&A,&B,&templen); mGraph.matrix[A][B]=templen; } in[1]=1; path[1]=1; //sava path Len[1]=0; for(i=2;i<=n;i++) { Len[i]=mGraph.matrix[1][i]; //Init the len if(Len[i]!=INFINITE)path[i]=1; } min=0; si=1; while(count<n-1) { tempmin=INFINITE; for(i=1;i<=n;i++) { if(in[i]==0&&Len[i]<tempmin) //find the smallest one { tempmin=Len[i]; si=i; } } in[si]=1; for(i=1;i<=n;i++) //updata the length { if(in[i]==0&&(tempmin+mGraph.matrix[si][i])<Len[i]) { Len[i]=tempmin+mGraph.matrix[si][i]; path[i]=si; } } count++; } mstack s; for(i=1;i<=n;i++) { temp=i; InitStack(&s); if(path[temp]==0) { printf("no path\n"); continue; } while(path[temp]!=1) { push(&s,path[temp]); temp=path[temp]; } printf("1-->"); while(s.bottom!=s.top) { printf("%d-->",pop(&s)); } printf("%d min length is %d\n",i,Len[i]); } } return 0; }
附上上面那张图的结果(V0节点为1,V1节点为2.。。。以此类推)