Dijkstra
这是一个按路径长度递增的次序产生最短路径的算法。Dijkstra并不是一下子就求出了v0到v8的最短距离,而是一步步求出它们之间顶点的最短路径,过程中都是基于已经求出的最短路径的基础上,求得更远顶点的最短路径,最终得到你要的结果。
首先给出代码,从代码的模拟运行中,去理解它的思想。
1 void ShortestPath_Dijkstra(MGraph G, int v0, Patharc *p, ShortPathTable *D) 2 { 3 int v,w,k,min; 4 int final[MAXVEX];//final[w]=1 5 for(v=0;v<G.numVertexes;v++) 6 { 7 final[v]=0; 8 (*D)[v]=G.arc[v0][v]; 9 (*P)[v]=0; 10 } 11 (*D)[v0]=0; 12 final[v0]=1; 13 for(v=1;v<G.numVertexes;v++) 14 { 15 min=INFINITY; 16 for(w=0;w<G.numVertexes;w++) 17 { 18 if(!final[w]&&(*D)[w]<min) 19 { 20 k=w; 21 min=(*D)[w]; 22 } 23 } 24 final[k]=1; 25 for(w=0;w<G.numVertexes;w++) 26 { 27 if(!final[w]&&(min+G.arc[k][w]<(*D)[w])) 28 { 29 (*D)[w]=min+G.arc[k][w]; 30 (*P)[w]=k; 31 } 32 } 33 } 34 }
下面我们先用Dijkstra思想来做一个例题,求出有向网G的v0顶点到其余顶点v最短路径P[v]及带权长度D[v]。
这个样例的源点为v0,终点为v8。
v0-v1 1 v0-v2 5 v1-v3 7 v1-v4 5 v1-v2 3 v2-v4 1 v2-v5 7 v3-v6 3
v3-v4 2 v4-v6 6 v4-v7 9 v4-v5 3 v5-v7 5 v6-v8 7 v6-v7 2 v7-v8 4
上述为连接的两个点之间的长度,其他任意两点不连接,所以长度为无穷大。
- 程序开始执行,第4行final数组是为了vo到某顶点是否已经求得最短路径的标记,如果v0到vw已经有结果,则final[w]=1。
- 第5~10行,是在对数据进行初始化的工作。此时final数组值均为0,表示所有的点都未求得最段路径。D数组为{65535,1,5,65535,65535,65535.65535,65535,65535}。因为v0和v1和v2的边权值为1和5.P数组全为0,表示目前没有路径。
- 第11行,表示v0到v0自身,权值和结果为0.D数组为{0,1,5,65535,65535,65535.65535,65535,65535}。第12行,表示v0点算是已经求得最短路径,因此final[0]=1。此时final数组为{1,0,0,0,0,0,0,0,0}。此时整个初始化工作完成。
- 第13~33行,为主循环,每次循环求得v0与一个顶点的最短路径。因此v从1而不是0开始。
- 第15~23行,先令min为65535的极大值,通过w循环,与D[W]比较找到最小值min=1,k=1。
- 第24行,由k=1,表示与v0最近的顶点是v1,并且由D[1]=1,直到此时V0到V1的最短距离是1。因此将V1对应的final[1]设置为1。此时final数组为{1,1,0,0,0,0,0,0,0}。
- 第25~32行是一循环,此循环甚为关键。它的目的是在刚才已经找到V0与V1的最短路径的基础上,对V1与其他顶点的边进行计算,得到V0与它们的当前最短距离。因为min=1,所以本来D[2]=5,现在V0->V1->V2=D[2]=min+3=4,V0->V1-V3=min+7=8,V0->V1->V4=D[4]=min+5=6,因此,D数组当前值{0,1,4,8,6,65535.65535,65535,65535}。而P[2]=1,P[3]=1,P[4]=1,它表示的意思是V0到V2、V3、V4点的最短路径它们的前驱均为V1,此时P数组值为:{0,0,1,1,1,0,0,0,0}。
- 重新开始循环,此时V=2。第15~23行,对w循环,注意因为final[0]=1和final[1]=1,由第18行!final[w]可知,V0与V1并不参与最小值的获取。通过循环比较,找到最小值min=4,k=2。
- 第24行,由k=2,表示已经求出V0到V2的最短路径,并且由D[2]=4,知道最短路径是4,其实如果你看一燕我上面写的D数组的取值,除了0、1两个已经有结果的值,最小的就是4了。因此将V2对应的final[2]设置为1,此时final数组为:{1,1,1,0,0,0,0,0,0}。
- 第25~32行。在刚才已经找到V0与V2的最短路径的基础上,对V2与其他顶点的边,进行计算,得到V0与它们的当前最短距离。因为min=4,所以本来D[4]=6,现在V0->V2->V4=D[4]=min+1=5,V0->V2->V5=D[5]=min+7=11,因此,D数组当前值为{0,1,4,8,5,11,65535,65535,65535}。而原本P[4]=2,P[5]=2,它表示V0到V4、V5点的最短路径他们的前驱均是V2。此时P数组值为:{0,0,1,1,2,2,0,0,0}。
- 重新开始循环,此时V=3。第15~23行,通过对w循环比较找到最小值min=5,k=4。
- 第24行,由k=4,表示已经求出V0到V4的最短路径,并且由D[4]=5,知道最短路径是5。因此将V4对应的final[4]设置为1。此时final数组为:{1,1,1,0,1,0,0,0,0}。
- 第25~32行。对V4与其他顶点的边进行计算,得到V0与他们的当前最短距离。因为min=5,所以本来D[3]=8,现在V0->V4->V3=D[3]=min+2=7,本来D[5]=11,现在V0->V4->V5=D[5]=min+3=8,另外V0-V4-V6=D[6]=min+6=11,V0->V4->V7=D[7]]=min+9=14,因此,D数组当前值为:{0,1,4,7,5,8,11,14,65535}。而原本P[3]=1,此时P[3]=4,原本P[5]=2,此时P[5]=4,另外P[6]=4,P[7]=4,它表示V0到V3、V5、V6、V7点的最短路径它们的前驱均是V4。此时P数组为:{0,0,1,4,2,4,4,4,0}。
- 重新循环开始,此时V=4。第15~23行,通过对w循环比较找到最小值min=7,k=3。
- 第24行,由k=3,表示已经求出V0到V3的最短路径,并且由D[3]=7,最短路径是7。因此将V3对应的final[3]设置为1。此时final数组为:{1,1,1,1,1,0,0,0,0}。
- 第25~32行,对V3与其他顶点的边进行计算,得到V0与他们的当前最短距离。因为min=7,所以本来D[6]=11,现在V0->V3->V6=D[6]=min+3=10,因此D数组当前值为:{0,1,4,7,5,8,10,14,65535}。原本P[6]=4,此时P[6]=3,它表示V0到V6的最短路径的前驱是V3。此时P数组为:{0,0,1,4,2,4,3,4,0}。
- 重新开始循环,此时V=5。第15~23行,通过对w循环比较找到最小值min=8,k=5。
- 第24行,由k=5,表示已经求出V0到V5最短路径,并且由D[5]=8,最短路径是8。因此将V5对应的final[5]设置为1。此时final数组为:{1,1,1,1,1,1,0,0,0}。
- 第25~32行,对V5与其他顶点的边进行计算,得到V0与他们的当前最短距离。因为min=11,所以本来D[7]=14,现在V0->V5->V7=D[7]=min+5=13,因此D数组当前值为:{0,1,4,7,5,8,10,13,65535}。原本P[7]=4,此时P[7]=5,它表示V0到V6的最短路径的前驱是V5。此时P数组为:{0,0,1,4,2,4,3,5,0}。
- 重新开始循环,此时V=6。第15~23行,通过对w循环比较找到最小值min=10,k=6。
- 第24行,由k=6,表示已经求出V0到V6最短路径,并且由D[6]=10,知道最短距离是10。因此将V6对应的final[6]设置为1。此时final数组为:{1,1,1,1,1,1,1,0,0}。
- 第25~32行,对V6与其他顶点的边进行计算,得到V0与它们的当前最短距离。因为min=10,所以本来D[7]=13,现在V0->V6->V7=D[7]=min+2=12,V0->V6->V8=min+7=17,因此D数组当前值为:{0,1,4,7,5,8,10,12,17}。原本P[7]=5,此时P[7]=6,P[8]=6,它们表示V0到V7和V8的最短路径的前驱是V6。此时P数组为:{0,0,1,4,2,4,3,6,6}。
- 重新开始循环,此时V=7。第15~23行,通过对w循环比较找到最小值min=12,k=7。
- 第24行,由k=7,表示已经求出V0到V7最短路径,并且由D[7]=12,知道最短距离是12。因此将V7对应的final[7]设置为1。此时final数组为:{1,1,1,1,1,1,1,1,0}。所以本来D[8]=17,现在V0->V7->V8=D[8]=min+4=16,因此D数组当前值为{0,1,4,7,5,8,10,12,16}。原本P[8]=6,此时P[8]=7,它表示V0到V8的最短路径的前驱是V7。此时P数组为{0,0,1,4,2,4,3,6,7}。
- 重新开始循环,此时V=8。第15~23行,通过循环对w比较找到最小值min=16,k=8。
- 第24行,由k=8,表示已经求出V0到V8最短路径,并且由D[8]=16,直到最短距离是16。D数组当前值为:{0,1,4,7,5,8,10,12,16}。因此将V8对应的final[8]设置为1。此时final数组为{1,1,1,1,1,1,1,1,1}。此时P数组不变,仍为{0,0,1,4,2,4,3,6,7}。
循环结束,得到最终的结果。此时final数组为:{1,1,1,1,1,1,1,1,1},它表示所有的顶点均完成了最短路径的查找工作。此时数组D为:{0,1,4,7,5,8,10,12,16},它表示V0到各个顶点的最短路径数,比如D[8]=1+3+1+2+3+2+4=16。此时的P数组为:{0,0,1,4,2,4,3,6,7},这串数字可能略为难理解一些。比如P[8]=7,它的意思是V0到V8的最短路径,顶点V8的前驱顶点是V7,再由P[7]=6表示V7的前驱是V6,P[6]=3,表示V6的前驱是3。这样就可以得到V0到V8的最短路径为V8<-V7<V6<-V3<-V4<-V2<-V1<-V0,即V0->V1->V2->V4->V3->V6->V7->V8。
其实最终返回的数组D和数组P,是可以得到V0到任意一个顶点的最短路径和路径长度的。例如V0到V8的最短路径并没有经过V5,但我们已经直到V0到V5的最短路径了。由V[5]=8可知它的路径长度为8,由P[5]=4可知V5的前驱顶点是V4,所以V0到V5的最短路径是V0->V1->V2->V4->V5
也就是说,我们通过迪杰斯特拉(Dijkstra)算法解决了从某个源点到其余各顶点的最短路径问题。从循环嵌套可以很容易得到此算法的时间复杂度为O(n^2)。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
typedef int Patharc[MAXVEX];//用于存储最短路径下标的数组 typedef int ShortPathTable[MAXVEX];//用于存储到各点最短路径的权值和 //Dijkstra算法,求有向图G的V0顶点到其余顶点V最短路径P[V]及带权长度D[V] //P[V]的值为前驱顶点下标,D[V]表示V0到V的最短路径长度和 void ShortestPath_Dijkstra(MGraph G, int v0, Patharc *p, ShortPathTable *D) { int v,w,k,min; int final[MAXVEX]; //final[w]=1表示求得顶点v0到vw的最短路径,final数组是为了v0到某顶点是否已经求得最短路径的标记,若v[0]到v[w]已有结果,则final[w]=1 for(v=0;v<G.numVertexes;v++)//数据初始化 { final[v]=0;//全部顶点初始化为未知最短路径状态 (*D)[v]=G.arc[v0][v];//将与v0点有连线的顶点加上权值 (*P)[v]=0;//初始化路径数组P为0 } (*D)[v0]=0;//v0至v0路径为0 final[v0]=1;//v0至v0不需要求路径 for(v=1;v<G.numVertexes;v++) { //开始主循环,每次求得v0到某个顶点的最短路径,因此v从1开始 min=INFINITY;//当前所知离v0顶点的最近距离 for(w=0;w<G.numVertexes;w++)//寻找离v0最近的顶点 { if(!final[w]&&(*D)[w]<min) { k=w; min=(*D)[w];//w顶点离v0顶点更近 } } final[k]=1;//将目前找到的最近的顶点置为1 for(w=0;w<G.numVertexes;w++)//修正当前最短路径及距离,此循环在于在v0到v1的基础上的最短路径的基础上,对v1与其他顶点的边进行计算,得到v0与它们的当前最短距离 {//如果经过v顶点的路径比现在这条路径的长度短的话 if(!final[w]&&(min+G.arc[k][w]<(*D)[w])) { (*D)[w]=min+G.arc[k][w];//修正当前路径长度 (*P)[w]=k; } } } }