数据结构——图最短路径问题

本文深入讲解Floyd-Warshall算法,一种用于计算带权图中任意两点间最短路径的经典算法。介绍了算法的基本原理,包括如何处理负边权的情况,以及详细的算法步骤和实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最短路径

我们把边带有权值的图称为带权图,边的权值可以表示
两点之间的距离。
一张图中任意两点间会有不同的路径相连。
最短路径就是指连接两点的这些路径中最短的一条。
在这里插入图片描述
有一点需要特别注意:边的权值可以为负。当出现负边权时,有些算法不适用。
dis[u][v]表示从u到v最短路径长度,w[u][v]表示连接u,v的边的长度。

Floyd-Warshall算法

简称Floyd(弗洛伊德)算法,是最简单的最短路径算法,可以计算图中任意两点间的最短路径。
Floyed的时间复杂度是O (n^3),适用于出现负边权的情况。
算法思想:
考虑任意两点(u和v)间的所有可能路径情况:
u直接到达v {u,v}。
u经过其他点到达v{u, vi, ……, v}。

算法策略:
如何穷尽Vi 到 Vj 的所有可能存在的路径?
可能的路径情况:
如果Vi 和Vj 邻接,{Vi , Vj }
通过一个其他顶点中转,表示为{Vi , Vk , Vj } ,其中k≠i、j。
通过两个其他顶点中转,可表示为
{Vi , Vk , Vl, Vj },其中k、l 与i、j均不等。
依次类推, Vi 到Vj 的路径最多可能通过n-2 个顶点中转可达,即除了路径的起点、终点,其他顶点都可能出现在该路径中。
在这里插入图片描述
Floyd算法设计:
1.利用矩阵完成路径检测过程

矩阵D(i) 包含中转顶点不超过 i(即中转顶点从0到 i) 的所有最短路径长度。

矩阵D(0) D(0)[i][j]:从V[i]到V[j],中间顶点可以是V[0]的最短路径长度。V[0]因为不确定是从0开始的还是从1开始的第一个点。所以用V[0]代替。

矩阵D(k) D(k)[i][j]:从V[i]到V[j],中间顶点序号不大于k的(即从V[0]到k)最短路径长度。

不断更新矩阵D。
……
直到最后D(n-1) D (n-1)[i][j]: 从V[I]到V[j]的最短路径长度。
2.路径长度矩阵序列求解过程
首先,设置初始状态:D(-1)(邻接矩阵)
然后,通过递推,求解每一个D(i)
矩阵D(k-1) --------> 矩阵D(k)过程
若不存在以k为中转点的vi到vj的最短路径:
D(k)[i][j] = D(k-1)[i][j];
若存在以k的vi到vj的最短路径:
D(k)[i][j] = D(k-1)[i][k] + D(k-1)[k][j];
3.如何记录最短路径的顶点序列?
用矩阵P(二维数组)来存储路径
P[i][j]存储当前最短路径更新时的中转点
最后以递归的方法来输出整个路径

大致代码

void Floyd( )
 {
  for( i=1; i<=n; ++i)
    for(j=1; j<=n; ++j) 
    {
       D[i][j] = G[i][j];    //第一步,初始化:用图G 的邻接矩阵初始化二维数组D。二维数组P 的元素初始化。    
       P[i][j] = -1;
    }
  for(k = 1; k<=n; ++k)
    for(i=1; i<=n; ++i)
      for(j=1; j<=n; ++j)

           if(D[i][j] >D[i][k] + D[k][j] )//第二步,对每个顶点Vk,若D[i][k]+D[k][j]< D[i][j],则用D[i][k]+D[k][j]更新D[i][j],并且修改P[i][j]为k。
          {
              D[i][j] = D[i][k] + D[k][j] ;          P[i][j] = k;
           }
}

利用数组P进行递归输出各条最短路径:
比如,输出B到A的路径:
因为P[2][1]为4,说明这条路径经过了4号顶点,即:顶点D。因此它是由B到D的最短路和D到A的最短路构成;
同样根据P[2][4]是3,可知它由B到C的最短路和C到D最短路构成,以此类推,因此通过递归就可以输出该条路径。
在这里插入图片描述

void print(int i,  int  j)
{
  if (P[i][j] == -1) return; 
  print( i,  P[i][j]); //前半段路
    cout << P[i][j]<<"->" ;
     print( P[i][j] , j); //后半段路

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杜康o

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值