所有结点对最短路径算法:Floyd-Warshall算法

背景知识

图简介

图由节点和边组成,边有方向的图称为有向图,边没有方向的图称为无向图,最短路径算法里可以把无向图视为双向连接的有向图。
边有权重的图称为有权图,边没有权重的图称为无权图,无权图可以视为边的权重均为1的图。

点对点最短路径

求图中任意(所有)两点之间的最短路径,

Floyd-Warshall算法

概述

点对点路径可以通过将每个点作为源点使用Dijkstra算法或者Bellman-Ford算法来求得,算法复杂度分别为O(V^3),O(E*V^2)。
Folyd-Warshall算法复杂度并没有降低,是O(V^3),但是Floyd的算法非常简洁、优雅,嗯,比前面说到的两个简洁了不只一点点。代码核心就三个循环 + 一个距离更新语句。

核心思想

循环V次,每次循环使用k作为中继点,k是循环的迭代系数,初始为0,代表当前循环体中使用的中继节点)分别计算所有点对点之间以k为中继点的距离,如果以k为中继点的距离更小,就更新最短路径。

算法步骤

dis[][]用于存储每个点对的距离,midNode[][]用于存储每个点对的中继点信息
Step1:初始化,dis[][]初始化为图中各边的权重,dis[i][i]初始化为0,没有边直接连接的点对初始化为INT_MAX,
Step2:遍历每个节点:对每个节点k,执行Step3
Step3:遍历所有点对(v,u),若dis[v][k], dis[k][u]均非INT_MAX,且dis[v][k] + dis[k][u] 小于 dis[v][u], 执行:dis[v][u] = dis[v][k] + dis[k][u];midNode[v][u] = k;
Step3:遍历同一节点的距离,即dis[i][i],如果dis[i][i]小于0,说明图中存在负环。

为什么Folyd算法能获得最短路径

其实最让人怀疑的是使用中继点更新距离时(dis[v][u] = dis[v][k] + dis[k][u])dis[v][k]或者dis[k][u]可能还没有获得最小值。下面就简单说一下为什么dis[k][u]已经获得最小值了。
假设已知一条最短路径,例如(1, 3, 5, 7, 2, 4),那么dis[1][4]最晚是在7作为中继点时获得最大路径(因为7是1->4路径上最后一个被遍历到的中间节点),假设dis[1][7],dis[7][4]已经获得了最小值,如果此时dis[1][4]如果没有获得最小值,那么dis[1][4]必然被更新为dis[1][7] + dis[7][4],dis[1][4]获得了最短路径,
下面只需要证明dis[1][7] 和dis[7][4]已经找到了最短路径
以7->4为例,因为已知(1, 3, 5, 7, 2, 4)是最短路径,所以(7, 2, 4)一定也是最短路径,所以在2作为中继点时,dis[7][4]一定会被更新为dis[7][2] + dis[2][4](因为一只2在7->4最短路径上,在2这里肯定要更新的)
同理因为(7, 2, 4)是最短路径,所以(7, 2)和(2, 4)也分别为7->2 和2->4的最短路径。
综上所述:Floyd算法一定会找到(1, 3, 5, 7 ,2 ,4)这条最短路径。

判断负环的原理

如果存在负环,假设点v在负环上,dis[v][v] 经过这条负环获得的距离小于0(dis[v][v]的初始值),会被更新为负值。因此通过判断每个节点自己到自己的距离是否有负值就可以判断负环了。

C++实现

#include <iostream>
#include <vector>

using namespace std;

class Solution
{
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值