最短路径-Dijkstra算法

来源:互联网 发布:淘宝卖家怎么屏蔽差评 编辑:程序博客网 时间:2024/06/02 02:32

最短路径

网图和非网图中,最短路径的含义是不同的。由于非网图它没有边上的权值,所谓的最短路径,其实就是指两顶点之间经过的边数最少的路径;

而对于网图来说,最短路径,是指两顶点之间经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点是源点,最后一个顶点是终点。显然,研究网图更具有实际意义,就地图来说,距离就是两顶点间的权值之和。而非网图完全可以理解为所有边的权值都为1的网。


Dijkstra算法

Dijkstra算法通常以邻接矩阵为存储结构。

它的基本思路是:设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径 , 就将加入到集合S中,直到全部顶点都加入到S中,算法就结束了),第二组为其余未确定最短路径的顶点集合(用U表示)按最短路径长度的递增次序依次把第二组的顶点加入S中。在加入的过程中,总保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。此外,每个顶点对应一个距离,S中的顶点的距离就是从v到此顶点的最短路径长度,U中的顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。可以看出是一种贪心的思想。


下面看具体数据和模拟过程


typedef struct{    VertexType vexs[MAXVEX];//顶点信息,string类型    EdgeType arc[MAXVEX][MAXVEX];//边的信息    int numVertexes,numEdges;//顶点数和边数}MGraph;

如上图所示的邻接矩阵,其中MAXVEX为最大顶点数,定义参数V0为0,Dijkstra算法代码如下:

#define MAXVEX 9#define INFINITY 0x3fffffff  typedef int Patharc[MAXVEX];//用于存储最短路径下标的数组typedef int ShortPathTable[MAXVEX];//用于存储到各点最短路径的权值之和void ShortestPath_Dijkstra(MGraph G,int v0,Patharc *p,ShortPathTable *D)//最短路求解,这里V0为0 {    int v,w,k,Min;    int Final[MAXVEX];//标记,=1表示求得顶点V0至Vw的最短路径    for(v=0;v<G.numVertexes;v++){//初始化数据        Final[v]=0;//全部顶点初始化为未知最短路径状态        (*D)[v]=G.arc[v0][v];//将与V0有连线的顶点加上权值        (*P)[v]=0;//初始化路径数组pre顶点均为起始点V0    }    (*D)[v0]=0;//v0至v0路径为0    Final[v0]=1;//v0至v0不需要求路径    for(v=1;v<G.numVertexes;v++){        Min=INFINITY;//初始化最小值为INF        for(w=0;w<G.numVertexes;w++){            if(!Final[w]&&(*D)[w]<Min){                k=w;                Min=(*D)[w];//w顶点离v0顶点更近            }        }        Final[k]=1;//将目前找到的最近的顶点位置置为1        for(w=0;w<G.numVertexes;++w){//修正当前最短路径及距离            //如果经过v顶点的路径比现在这条路径的长度短的话            if(!Final[w]&&(Min+G.arc[k][w]<(*D)[w])){                (*D)[w]=Min+G.arc[k][w];//修改当前路径长度                (*P)[w]=k;            }        }    }}

1.程序开始运行,Final数组是为了V0到某顶点是否已经求得最短路径的标记,如果V0到Vw已经有结果,则Final[w]=1


2.第9~13行对数据初始化。此时Final数组值均为0,表示所有的点都未求得最短路径。D数组为{inf,1,5,inf,inf,inf,inf,inf,inf}。因为V0与V1和V2的边权值为1和5。P数组全为0,表示目前没有路径。


3.第14行,表示V0到V0自身,权值和结果为0。D数组为{0,1,5,inf,inf,inf,inf,inf,inf}。第15行,表示V0点算是已经求得最短路径,因此Final[0]=1。此时Final数组为{1,0,0,0,0,0,0,0,0}。此时整个初始化工作完成。


4.下面是主循环,每次循环求得V0与一个顶点的最短路径。因此V从1而不是0开始。


5.先令Min为inf,通过w循环,与D[w]比较找到最小值Min=1,k=1。


6.第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}。


7.第25~32行是一循环,此循环甚为关键。它的目的是在刚才已经找到V0与V1的最短路径的基础上,对V1与其他顶点的边进行计算,得到V0与它们的当前最短距离,如下图所示。因为min=1,所以本来D[2]=5,现在v0->v1->v2=D[2]=min+3=4,v0->v1->v3=D[3]=min+7=8,v0->v1->v4=D[4]=min+5=6,因此,D数组当前值为{0,1,4,8,6,inf,inf,inf,inf}。而P[2]=1,P[3]=1,P[4]=1,它表示的意思是V0到V2、V3、V4点的最短路径它们的前驱均是V1。此时P[]数组值为:{0,0,1,1,1,0,0,0,0}。


8.重新开始循环,此时V=2。下面对w循环,注意因为Final[0]=1和Final[1]=1,由!Final[w]可知,V0与V1并不参与最小值的获取。通过循环比较,找到最小值min=4,k=2。


9.第24行,由k=2,表示已经求出V0到V2的最短路径,并且由D[2]=4,知道最短距离是4。因此将V2对应的Final[2]设置为1,此时Final数组为:{1,1,1,0,0,0,0,0,0}。


10.第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,inf,inf,inf}。而原本P[4]=1,此时P[4]=2,P[5]=2,它表示V0到V4、V5点的最短路径它们的前驱均是V2。此时P数组值为:{0,0,1,1,2,2,0,0,0}。


11.重新开始循环,此时V=3。第17~21行,通过对w循环比较找到最小值min=5,k=4。


12.第24行,由k=4,表示已经求出V0到V4的最短路径,并且由D[4]=5,知道最短距离是5。因此将v4对应的Final[4]设置为1。此时Final数组为:{1,1,1,0,1,0,0,0,0}。


13.第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,inf}。而原本P[3]=1,此时P[3]=4,原本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}。


14.之后的循环就完全类似了。得到最终的结果,如下图所示。此时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的前驱是v3。这样就可以得到,v0到v8的最短路径为v0->v1->v2->v4->v3->v6->v7->v8。



总结

       上面的算法最终返回的数组D和数组P,是可以得到V0到任意一个顶点的最短路径和路径长度的。例如v0到v8的最短路径并没有经过v5,但我们已经知道v0到v5的最短路径了。由D[5]=8可知它的路径长度为8,由P[5]=4可知v5的前驱顶点是v4,所以v0到v5的最短路径是v0->v1->v2->v4->v5。

       也就是说我们通过Dijkstra算法解决了从某个源点到其余各顶点的最短路径问题。从循环嵌套可以得到此算法的时间复杂度为O(n^2)

       如果我们还需要知道v3到v5、v1到v7这样的任意一顶点到其余所有顶点的最短路径怎么办呢?可以再原来Dijkstra算法的基础上,再来一次循环,此时算法的复杂度为O(n^3)

       求所有顶点到所有顶点还有一种非常简洁优雅的算法------Floyd算法,Dijkstra算法使用的前提是图中路径长度必须大于等于0但是Floyd算法则仅仅要求没有总和小于0的环路就可以了。