8、每对顶点之间的最短路径,弗洛伊德(Floyd)算法

顶点对之间的最短路径是指:对于给定的有向网G=(V,E),要对G中任意一对顶点有序对V、W(V≠W),找出V到W的最短距离和W到V的最短距离。

    解决此问题的一个有效方法是:轮流以每一个顶点为源点,重复执行迪杰斯特拉算法n次,即可求得每一对顶点之间的最短路径,总的时间复杂度为O(n3)。

弗洛伊德(Floyd)提出了另外一个求图中任意两顶点之间最短路径的算法,虽然其时间复杂度也是 O(n3),但其算法的形式更简单,易于理解和编程。

1、算法基本思想

弗洛伊德算法仍然使用图的邻接矩阵arcs[n+1][n+1]来存储带权有向图。算法的基本思想是:设置一个n x n的矩阵A(k),其中除对角线的元素都等于0外,其它元素a(k)[i][j]表示顶点i到顶点j的路径长度,K表示运算步骤。开始时,以任意两个顶点之间的有向边的权值作为路径长度,没有有向边时,路径长度为∞,当K=0时, A (0)[i][j]=arcs[i][j],以后逐步尝试在原路径中加入其它顶点作为中间顶点,如果增加中间顶点后,得到的路径比原来的路径长度减少了,则以此新路径代替原路径,修改矩阵元素。具体做法为:  

第一步,让所有边上加入中间顶点1,取A[i][j]与A[i][1]+A[1][j]中较小的值作A[i][j]的值,完成后得到A(1),

第二步,让所有边上加入中间顶点2,取A[i][j]与A[i][2]+A[2][j]中较小的值,完成后得到A(2)…,如此进行下去,当第n步完成后,得到A(n),A(n)即为我们所求结果,A(n)[i][j]表示顶点i到顶点j的最短距离。     

因此,弗洛伊德算法可以描述为:

       A(0)[i][j]=arcs[i][j];               //arcs为图的邻接矩阵

       A(k)[i][j]=min{A(k-1)[i][j],A(k-1) [i][k]+A(k-1) [k][j]}

其中 k=1,2,…,n

定义一个n阶方阵序列:

           D(-1),  D(0),  …,  D(n-1).

   其中  D(-1) [i][j] = G.arcs[i][j];

             D(k) [i][j] = min { D(k-1)[i][j],D(k-1)[i][k] + D(k-1)[k][j] },k = 0,1,…, n-1 

   D(0) [i][j]是从顶点vi 到vj , 中间顶点是v0的最短路径的长度,    

  D(k) [i][j]是从顶点vi 到vj ,  中间顶点的序号不大于k的最短路径长度, 

  D(n-1)[i][j]是从顶点vi 到vj 的最短路径长度。

2、算法的C语言描述

3、算法的完整实现

#define INF 32767            //INF表示无穷

#define MAXV 10

#include "stdio.h"

//以下是输出最短路径的算法dispath,其中ppath()函数在path中递归输出从顶点vi到vj的最短路径。

void ppath(int path[][MAXV],int i,int j)

{ int k;

  k=path[i][j];

  if(k==i)  //path[i][j]=i时,顶点vi和vj之间无中间顶点,也就是说找到了始节点

         return;

  ppath(path,i,k);

  printf("v%d,",k);

  ppath(path,k,j);

}

void dispath(int A[][MAXV],intpath[][MAXV],int n)

{ int i,j;

for(i=0;i<n;i++)

        for(j=0;j<n;j++)

        {

              if(A[i][j]==INF)

               {

                 if(i!=j)

                 printf("从顶点v%d到v%d没有路径:\n",i,j);

               }//if

          else

          {

               printf("从顶点v%d到v%d的路径为:",i,j);

               printf("v%d,",i);

               ppath(path,i,j);

               printf("v%d,",j);

               printf("路径长度为: %d ^-^\n",A[i][j]);

          }//else

        }

}

void floyd(int cost[MAXV][MAXV],int n)

{

int A[MAXV][MAXV],path[MAXV][MAXV];

int i,j,k;

for(i=0;i<n;i++)         //赋值A0[i][j]和path0[i][j]

        for(j=0;j<n;j++)

  {

          A[i][j]=cost[i][j];

          if(cost[i][j]<INF)

                path[i][j]=i; //初始化路径矩阵,原来有路

          else

                path[i][j]=-1; //原来没有路

       }//for

  for(k=0;k<n;k++)        //向vi与vj之间中n次加入中间顶点vk

        for(i=0;i<n;i++)

          for(j=0;j<n;j++) //求min{Ak[i][j],Ak+1[i][k]+Ak+1[k][j]}

               if(A[i][j]>(A[i][k]+A[k][j]))

                {

                     A[i][j]=A[i][k]+A[k][j];

                     path[i][j]=k;//表示从i节点到j节点,要经过k节点。

                }//if,for

  dispath(A,path,n);      //输出最短路径

}//floyd

int inputM(int cost[][MAXV])

{

int n,e,w,h,z;

/*

printf("Please input 顶点数和边数\n");

scanf("%d%d",&n,&e);

*/

n=3;

e=5;

for(int i=0;i<n;i++)

       for(intj=0;j<n;j++)

       {

              if(i==j)cost[i][j]=0;

              elsecost[i][j]=INF;

       }

/*

printf("please int the 邻接矩阵的值(起点(数字) 终点(数字) 权值(数字)):\n");

for(int i=0;i<e;i++)

 {

       scanf("%d%d%d",&h,&z,&w);

       cost[h][z]=w;

 }//for

*/

cost[0][1]=4;

cost[0][2]=11;

cost[1][0]=6;

cost[1][2]=2;

cost[2][0]=3;

return n;

}

int main()

{

int n;      

int cost[MAXV][MAXV];

n=inputM(cost); //返回顶点数

floyd(cost,n);

}

4、实例

  有向图如下:

其D和P矩阵变化过程如下:

5、注意点:

a) Floyd算法允许图中有带负权值的边,但不许有包含带负权值的边组成的回路。

发布了278 篇原创文章 · 获赞 31 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/hopegrace/article/details/104551137