最短路径:
对于网图来说,最短路径,是指两顶点之间经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点是源点,最后一个顶点是终点。
对于非网图来说,完全可以理解为所有边的权值都为1的网。
两种算法:迪杰斯特拉(Dijkstra)算法,弗洛伊德(Floyd)算法。
/*迪杰斯特拉算法实现*/
#define MAXVEX 9
#define INFINITY 65535
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的最短路径
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不需要求路径
/*开始主循环,每次求得v0到某个v顶点的最短路径*/
for(v = 1;v<g.numVertexes;v++)
{
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++)//修正当前最短路径及距离
{
/*如果经过v顶点的路径比现在这条路的长度短的话*/
if(!final[w]&&(min+G.arc[k][w]<(*D)[w])
{/*说明找到了更短的路径,修改D[w]和P[w]*/
(*D)[w] = min + G.arc[k][w];//修改当前路径长度
(*P)[w] = k;
}
}
}
}
以上的算法我们最终返回的数组D和数组P,是可以得到v0到任意一个顶点的最短路径和路径长度的。
我们通过迪杰斯特拉算法解决了从某个源点到其余各个顶点的最短路径问题。从以上的循环嵌套我们可以知道此算法的时间复杂度为O(n^2).
如果我们需要知道v3到v5、v1到v7这样的任一顶点到其余所有顶点的最短路径,此时最简单的方式就是对每个顶点当做源点进行一次迪杰斯特拉算法,等于在原有算法的基础上,再循环一次,此时整个算法的时间复杂度就成了O(n^3).
/*弗洛伊德(Floyd)算法的实现*/
/*代码如下,注意因为是求所有顶点到所有顶点的最短路径,因此Pathmatirx和ShortPathTable都是二维数组*/
typedef int Pathmatirx[MAXVEX][MAXVEX];
typedef int ShortPathTable[MAXVEX][MAXVEX];
/*Floyd算法,求网图G中各顶点w最短路径P[v][w]及带权长度D[v][w]*/
void ShortestPath_Floyd(MGraph G,Pathmatirx *P,ShortPathTable *D)
{
int v,w,k;
for(v=0;v<G.numVertexes;v++)//初始化D与P
{
for(w=0;w<G.numVertexes;w++s)
{
(*D)[v][w] = G.matirx[v][w];//D[v][w]值即为对应点间的权值
(*P)[v][w] = w;//初始化P
}
}
for(k = 0;k<G.numVertexes;++k)
{
for(v=0;v<G.numVertexes;++v)
{
for(w=0;w<G.numVertexes;++w)
{
if((*D)[v][w]>(*D)[v][k]+(*D)[k][w]) {/*如果经过下标为k顶点路径比原两点间路径更短,将当前两点间权值设置为更小的一个*/
(*D)[v][w] = (*D)[v][w]+(*D)[k][w];
(*p)[v][w] = (*P)[v][k];//路径设置经过下标为K的顶点
}
}
}
}
}
这个算法的主要的公式就是:
D^0[v][w] = min{D^(-1)[v][w],D^(-1)[v][0]+D^(-1)[0][v]}
如何由得出的P数组得出具体的路径:
以上面右图为例:
以V0到V8为例,可见上面的P[0][8]=1,得到要经过顶点v1,然后将0换成1,
得到P[1][8] = 2,说明要经过V2,然后将2取代1得到P[2][8]=4,
以此类推,得到路径v0——>v1——>v2——>v4——>v3——>v6——>v7——>v8
求最短路径的现实代码如下:
for(v = 0;v=G.numVertexes;++v)
{
for(w = v+1;w<G.numVertexes;w++)
{
printf(“v%d-v%d weight: %d “,v,w,D[v][w]);
k = P[v][w];//获取第一个路径的顶点下标
printf(” path: %d”,v);//打印源点
while(k!=w)//如果路径顶点下标不是终点
{
printf(“——>V%d”,k);//打印路径顶点
k = P[k][w];
}
printf(“——>V%d”,w);//打印终点
}
printf(“\n”);
}
根据以上的代码我们可以看到Floyd算法是一个简单的二重循环初始化加一个三重循环的修正。
由于本是是一个三重循环,所以时间复杂度为O(n^3)。如果面临要求所有顶点至所有顶点的最短路径的问题时,Floyd算法是非常好的算法。