算法导论小结(12)-每对顶点间最短路径问题

本文详细介绍了《算法导论》第25章的内容,主要讨论了两种求解每对顶点间最短路径问题的算法:Floyd-Warshall算法和Johnson算法。Floyd-Warshall算法运行时间为Θ(V^3),允许存在负权边但假设不存在负权回路。Johnson算法则通过重赋权将负权边转换为非负权边,再用Dijkstra算法解决。
摘要由CSDN通过智能技术生成

By:             潘云登

Date:          2009-7-30

Email:         intrepyd@gmail.com

Homepage: http://blog.csdn.net/intrepyd

Copyright: 该文章版权由潘云登所有。可在非商业目的下任意传播和复制。

对于商业目的下对本文的任何行为需经作者同意。


写在前面

1.          本文内容对应《算法导论》(2)》第25章。

2.          主要介绍了两种求解每对顶点间最短路径问题的算法:Floyd-Warshall算法和Johnson算法。

3.          希望本文对您有所帮助,也欢迎您给我提意见和建议。


在单源最短路径问题中, Dijkstra算法以贪心算法的形式利用了最短路径的最优子结构。在这里,将以动态规划的形式求解每对顶点间的最短路径。首先,推导一种最优子结构的递归解。设lij(m)是从顶点i到顶点j的至多包含m条边的任何路径的权值最小值。当m=0时,从ij存在一条不包含边的最短路径当且仅当i=j,因此,当i=j时,lij(0) =0;当ij时,lij(0) =∞。m1,先计算lij(m-1),即从ij的最短路径的权至多包含m-1条边,以及从ij的至多包含m条边的路径的最小值,后者是通过计算j的所有可能前趋k而得到的,然后取二者中的最小值作为lij(m)。因此,递归定义lij(m)=min1kn{ lij(m-1)+wkj}。如果图中不包含负权回路,那么ij的最短路径至多包含n-1条边,L(n-1) 即为最终的解。而L(1) 为图的邻接矩阵W。这样,通过四重循环(2..n-1; k=1..n; i=1..n; j=1..n)就能够自底向上地计算L(2)L(3)L(n-1)。这种算法的运行时间为Θ(n4)n=|V|


算法

Floyd-Warshall 

Floyd-Warshall算法的运行时间为Θ(V3),它同样允许存在负权边,但假设不存在负权回路。该算法考虑的最优子结构与上述描述类似,即最短路径的子路径是最短路径。但是,它对中间路径的范围加以限制,使其增长与最短路径的最大边数的增长同步。令dij(k)=为从顶点i到顶点j、且满足所有中间顶点属于集合{1,2,…,k}的一条最短路径的权值,定义递归解:如果k=0dij(k)=wij;如果k1dij(k)=min{ dij(k-1), dik(k-1)+ dkj(k-1)}。可以想象,当k=1时,ij的最短路径或者由边(i,j)构成,或者由边(i,1)(1,j)构成。当k=2时,ij的最短路径可能由子路径i2和子路径2j构成,而子路径2j又可能由边(2,1)(1,j)构成,即k=2的解包含了k=1的解。以此类推,当k=n时,即可得到每对顶点间最短路径的权值。

为了构造最短路径,定义πij(k)为从i出发的一条最短路径上顶点j的前趋,而这条路径上的中间顶点属于集合{1,2,…,k}。定义πij(k)的递归公式,对于k=0,如果i=jwij=∞,πij(0) =NIT(-1);如果ijwij<∞,πij(0) =i。对于k1,如果dij(k-1)dik(k-1)+ dkj(k-1),πij(k) =πij(k-1);如果dij(k-1)>dik(k-1)+ dkj(k-1),πij(k) =πkj(k-1)

void floyd_warshall(adjacency_matrix *graphic)

{

    int i, j, k;

    int **temp_distance=NULL;

   

    temp_distance = malloc(sizeof(int *)*graphic->vertex_number);

    for(i=0; i<graphic->vertex_number; i++)

          temp_distance[i] = malloc(sizeof(int)*graphic->vertex_number);

 

    for(i=0; i<graphic->vertex_number; i++)

    {

          for(j=0; j<graphic->vertex_number; j++)

          {

              temp_distance[i][j] = graphic->weight[i][j];

              graphic->distance[i][j] = graphic->weight[i][j];

              if(i==j || graphic->weight[i][j]==INF)

                  graphic->ancestor[i][j] = -1;

              else

                  graphic->ancestor[i][j] = i;

          }

    }

 

    for(k=0; k<graphic->vertex_number; k++)

    {

          for(i=0; i<graphic->vertex_number; i++)

          {

              for(j=0; j<graphic->vertex_number; j++)

              {

                  if(graphic->distance[i][j]

                       > temp_distance[i][k]+temp_distance[k][j])

                  {

                       graphic->distance[i][j]

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

                       graphic->ancestor[i][j] = graphic->ancestor[k][j];

                  }

               }

          }

 

          for(i=0; i<graphic->vertex_number; i++)

              for(j=0; j<graphic->vertex_number; j++)

                  temp_distance[i][j] = graphic->distance[i][j];

    }

}


Johnson

算法

Johnson算法把图G的每个顶点轮流作为源点调用Dijkstra算法。由于Dijkstra算法不允许存在负权边,因此需要对图G进行重赋权,使得对于所有的边(u, v),新的权w’(u, v)是非负的。同时,要求对所有顶点对u, vV,路径p是利用加权函数wuv的一条最短路径,当且仅当p也是利用加权函数w’uv的一条最短路径。

         给定一个带权有向图G=(V, E),加权函数为w: E->R,据此构造一个新图G’=(V’, E’),其中对某个新顶点s不属于VV’=VU{s}E’=EU{(s, v) :vV}。扩展加权函数w,使得对所有vV,有w(s,v)=0。假设GG’不含负权回路,定义对所有vV’h(v)=δ(s, v),它可由Bellman-Ford算法求得。根据三角不等式有h(v)h(u)+w(u,v),因此,定义w’(u, v)=w(u, v)+h(u)-h(v)0,满足上述条件。由于调用了Bellman-Ford算法,Johnson算法或者报告输入图中存在一条负权回路,或者返回|V|*|V|的矩阵D=dij,其中dij=δ(i, j)

int johnson(adjlist *graphic)

{

    int i, j, result;

    adjlist *g;

    lnode *temp_lnode=NULL;

 

    g = malloc(sizeof(adjlist));

    new_graphic(g, graphic->vertex_number+1);

    copy_graphic(graphic, g);

    for(i=0; i<g->vertex_number-1; i++)

          insert_edge(g, g->vertex_number-1, i, 0);

 

    if(bellman_ford(g, g->vertex_number-1))

    {

          for(i=0; i<g->vertex_number; i++)

              g->h[i] = g->distance[i];

 

          for(i=0; i<g->vertex_number; i++)

          {

              temp_lnode = g->vlist[i].link;

              while(temp_lnode != NULL)

              {

                  temp_lnode->weight =

                       temp_lnode->weight + g->h[i] - g->h[temp_lnode->index];

                  temp_lnode = temp_lnode->next;

              }

          }

 

          for(i=0; i<graphic->vertex_number; i++)

          {

              dijkstra(g, i);

              for(j=0; j<graphic->vertex_number; j++)

              {

                  graphic->distance_uv[i][j]

 = g->distance[j] + g->h[j] - g->h[i];

                  graphic->ancestor_uv[i][j] = g->ancestor[j];

              }

          }

 

          result = 1;

    }

    else

    {

          /*A negative-weight cycle exists!*/

          result = 0;

    }

    free_graphic(g);

    free(g);

 

    return result;

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值