前言

本章节,我们考虑“如何找到一个图中所有结点之间的最短路径”问题。我们通过运行|V|次上章节的单源最短路径算法解决所有结点对之间的最短路径问题,只需要每一次使用一个不同的结点作为源结点即可。显示复杂度有点高。本章要讨论的“更好”的算法,都是邻接矩阵来表示图。下面给出邻接矩阵表示的一些约定:

  1. 假定输入图G=(V,E)有n个结点,因此n=|V|。
  2. 使用大写字母来表示矩阵,如W、L或D,而用带下标的小写字母来表示矩阵中的元素,如wij、lij或dij。
  3. 一些矩阵将有带括号的上标,如L(m)=(lij(m))L^{(m)}=(l_{ij}^{(m)})L(m)=(lij(m)​)或D(m)=(dij(m))D^{(m)}=(d_{ij}^{(m)})D(m)=(dij(m)​)用来表示迭代。
  4. 对于一个给定的n×n矩阵A,假定矩阵的维度n存储在属性A.rows中。

最短路径和矩阵乘法

本节讨论有向图G=(V,E)上所有结点对最短路径问题的一种动态规划算法。在动态规划的每个大循环里,将调用一个与矩阵乘法非常相似的操作,因此该算法看上去就像是重复的矩阵乘法。设计动态规划算法的步骤如下:

  1. 分析最优解的结构。
  2. 递归定义最优解的值。
  3. 自底向上计算最优解的值。

最短路径的结构

  • 假定用邻接矩阵来表示输入图,即W=(wij)。
  • 从结点i到结点j的一条最短路径p,假定p至多包含m条边,且m为有限值
  • 还假定没有权重为负值的环路。
  • 如果i=j,则p的权重为0且不包含任何边。
  • 如果结点i和结点j不同,则将路径p分解为:i→p1k→ji \xrightarrow[]{p^1} k\xrightarrow[]{}jip1​k​j其中路径p’至多包含m-1条边。

可知p’是从结点i到结点k的一条最短路径,因此δ(i,j)=δ(i,k)+wkjδ(i,j)=δ(i,k)+w _{kj}δ(i,j)=δ(i,k)+wkj​。

所有结点对最短路径问题的递归解

现在设lij(m)l_{ij}^{(m)}lij(m)​为从结点i到结点j的至多包含m条边的任意路径中的最小权重.当m>0,因此递归定义:
lij(m)=min⁡1≤k≤n(lik(m−1)+wkj)l_{ij}^{(m)} = \min_{1\le k \le n}(l_{ik}^{(m-1)} + w_{kj})lij(m)​=1≤k≤nmin​(lik(m−1)​+wkj​)

自底向上计算最路径权重

将上述递归式,转为算法,给定W, L作为L(m-1),L’作为L(m)。

EXTEND-SHORTEST-PATHS(L,W)n=L.rowslet L'=(l'[i][j]) be a new n×n matrixfor i=1 to nfor j=1 to nl'[i][j]=∞for k=1 to nl'[i][j]=min(l'[i][j],l[i][k]+w[k][j])return L'

即通上述程序依次计算出矩阵序列L(1)=W,L(2),…,L(n-1)。因此总的算法有3层嵌套的for循环,该算法的运行时间为Θ(n3)。

该算法与矩阵乘法的关系(略)

Floyd-Warshall算法

本节将使用一种不同的动态规划公式来解决所有结点对最短路径问题,所产生的算法称为Floyd-Warshall算法,其运行时间为Θ(V3)。

最短路径结构

假定图G的所有结点为V={1,2,…,n},考虑其中的一个子集{1,2,…,k},这里k是某个小于n的整数。对于任意结点对i,j∈V,考虑从结点i到结点j的所有中间结点均取自集合{1,2,…,k}的路径,并且设p为其中权重最小的路径(路径p是简单路径)。

Floyd-Warshall算法利用了路径p和从i到j之间中间结点均取自集合{1,2,…,k-1}的最短路径之间的关系。该关系依赖于结点k是否是路径p上的一个中间结点:

所有结点对最短问题的一个递归解

设dij(k)d_{ij}^{(k)}dij(k)​为从结点i到结点j的所有中间结点全部取自集合{1,2,…,k}的一条最短路径的权重。当k=0时,从结点i到结点j的一条不包括编号大于0的中间结点的路径将没有任何中间结点,这样的路径最多只有一条边,因此dij(0)=wijd_{ij}^{(0)}=w_{ij}dij(0)​=wij​。根据上面的讨论,递归定义dij(k)d_{ij}^{(k)}dij(k)​如下:
dij(k)={wijif k=0min⁡(dij(k−1),dik(k−1)+dkj(k−1))if k≥0d_{ij}^{(k)} = \begin{cases} w_{ij} &\text{if } k=0 \\ \min(d_{ij}^{(k-1)},d_{ik}^{(k-1)} + d_{kj}^{(k-1)}) &\text{if } k \ge 0 \end{cases}dij(k)​={wij​min(dij(k−1)​,dik(k−1)​+dkj(k−1)​)​if k=0if k≥0​

自底向上计算最短路径权重

根据上图的递归解,以W为输入

以下图为例

其过程如下

  • D矩阵用于存储权值
  • 前驱矩阵Ⅱ用于存储得到这个权值的前驱节点
  1. 从D矩阵可看出,4节点到5节点最小权值为-2
  2. 前驱矩阵Ⅱ, Ⅱ[4][5] 表示 5节点 <- 1节点, 再看 Ⅱ[4][1] 1节点 <- 4节点
  3. 得 4节点到5节最短路径为 4节点->1节点->5节点

用于稀疏图的Johnson算法

Johnson 算法是用来解决在有负权重边图里的最短路径问题的,它主要了结合 Dijkstra 算法和 Bellman-Ford 算法。其算法复杂度为O(V2·lgV+V·E)。

如上图,其中 0 -> 1 是负数的,是不能使用 Dijkstra 去求最短路径的。

这时我们可能会想到把全部的边都加上 5 那大家不就都变成正数了?使用 Dijkstra 求完最短路径后再减回 5 那答案不就求到了么?


上图,S->T有两条路径

  • s-a-b-t 最短路径为3
  • s-t 路径为4

如果用给边加上值后再减去值的方式(加1),显示最短路径会变成s-t. 因此是种不正确的方式。

Johnson算法中是通过 Bellman-Ford 的方法是给每个节点设置一个值,用这些节点的值去做 reweight。

如图添加一个虚拟的节点4,到每个结点的距离是权值0. 使用Bellman-ford 去求这个虚拟节点到每个节点的最短距离

节点 h[x]
0 0
1 -5
2 -1
3 0

有了这些 h[x] 值后就可以对每条边进行 reweight 操作了,最终的路径应该要加上开始节点v0v_0v0​的 h 值,再减去结束节点的 h 值。
w(p)=w(p)+h(v0)−h(vk)w(p) = w(p) + h(v_0) - h(v_k)w(p)=w(p)+h(v0​)−h(vk​)

Johnson 算法步骤

  1. 添加虚拟节点到这个图里,并添加指向所有节点的虚拟边,这些边的权重为 0
  2. 以虚拟节点节点为起点,运行 Bellman-Ford 算法,求出到每个节点的最短距离,这些最短距离为每个节点的 h值
  3. 用上面求出的 h 值去更新图里的边,使得 w(p)=w(p)+h(v0)−h(vk)w(p) = w(p) + h(v_0) - h(v_k)w(p)=w(p)+h(v0​)−h(vk​)
  4. 移除添加的虚拟节点和边
  5. 在每个节点运行 Dijkstra 计算到其它节点的最短距离

主要参考

《算法导论-上课笔记12:所有结点对的最短路径问题》
《算法: Johnson 算法》
《All-Pairs Shortest Paths》

《算法导论3rd第二十五章》所有结点对的最短路径问题相关推荐

  1. 《算法导论3rd第十五章》动态规划

    前言 和分治法一样, 动态规划 (dynamic programming)是通过组合子问题的解而解决整个问题的.分治法是将问题划分成一些独立的子问题,递归地求解各子问题,然后合并子问题的解而得到原问题 ...

  2. 《算法导论3rd第十九章》斐波那契堆

    前言 第六章堆排序使用了普通的二叉堆性质.其基本操作性能相当好,但union性能相当差. 对于一些图算法问题,EXTRACT-MIN 和DELETE操作次数远远小于DECREASE-KEY.因此有了斐 ...

  3. 《算法导论3rd第十六章》贪心算法

    前言 适用于最优化问题的算法往往包含一系列步骤,每个步骤都面临多种选择.使用动态规划解决最优化问题,相当于计算出每咱选择,浪费大量效率.对于"特定"下的最优化问题,可以使用更简单更 ...

  4. 【正点原子STM32连载】 第二十五章 TFT-LCD(MCU屏)实验 摘自【正点原子】STM32F103 战舰开发指南V1.2

    1)实验平台:正点原子stm32f103战舰开发板V4 2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420 3)全套实验源码+手册+视 ...

  5. 第二十五章补充内容 3 assert()宏

    //第二十五章补充内容 3 assert()宏 //有的编译器还提供了assert()宏,这个宏在许多书中被翻译为断言,它的作用是当assert()的参数为真时,返回真,假如参数值为假,那么它将执行某 ...

  6. 25 linux ndk 头文件_正点原子Linux第二十五章RTC实时时钟实验

    1)资料下载:点击资料即可下载 2)对正点原子Linux感兴趣的同学可以加群讨论:935446741 3)关注正点原子公众号,获取最新资料更新 第二十五章RTC实时时钟实验 实时时钟是很常用的一个外设 ...

  7. 鸟哥的Linux私房菜(基础篇)- 第二十五章、 Linux 备份策略

    第二十五章. Linux备份策略 最近升级日期:2009/09/18 万一不幸你的 Linux 被黑客入侵了.或是你的 Linux 系统由於硬件关系 (不论是天灾还是人祸) 而挂掉了!这个时候,请问如 ...

  8. 第二十五章补充内容 5 不能为0的变量

    // 第二十五章补充内容 5 不能为0的变量 /*#define DEBUG #include <iostream> #include <string> using names ...

  9. 第二十五章:重新吃上饭的李恪

    第二十五章:重新吃上饭的李恪 可能李丽质永远都不会忘记了自己饿肚子饿了很久以来的第一顿饭吧. 吃的非常的香,吃完这顿饭以后,李丽质开始也参与到了洗皮衣的过程中,不过由于他个子比较小,所以只能做一些简单 ...

最新文章

  1. python日历提醒_Python之时间:calender模块(日历)
  2. 用lisp编写串口助手源代码_实战用python来写个串口助手--界面篇
  3. 013_html水平线
  4. 让你完全理解base64是怎么回事
  5. 深入理解Hadoop集群和网络
  6. 光流 | OpticalFlow博客资源
  7. 三种方式让你轻松监控 EntityFramework 中的 sql 流转
  8. java中多态,instanceof关键字
  9. 语音信号处理-python
  10. 《Hexo: 从零开始编写自己的主题》2. 入门Hexo主题编写
  11. 基于jsp+mysql+Spring+SpringMVC+mybatis的大学生缴费系统-计算机毕业设计
  12. 树莓派:64位 RPI OS(Bullseye) 更换国内源
  13. 日志管理(spring AOP切面拦截)
  14. 数字经济发展现状_(我的)数字媒体的现状
  15. Android开发之ExpandableListView可拓展列表和子item左滑结合
  16. c语言sqar是double,C语言怎么编写正弦波
  17. matlab画出拟合的曲线,matlab画出拟合曲线
  18. 自然月合同月 生成费用
  19. 网络安全法实施 个人信息保护立法还需做什么?
  20. 数据仓库基础理论笔记

热门文章

  1. 景区WiFi覆盖方案
  2. 注册表键值的导出与导入
  3. 服务器改为电脑要修改什么,怎么样更改电脑服务器名
  4. 开源网络文件管理工具SmarkNetDisk
  5. kinhdown引擎(lua)
  6. WPF中的3D Wireframe
  7. 前端基础入门之css像素与视口和媒体查询
  8. JVM<一>内存管理[三]垃圾回收器
  9. Linux下配置Popush——陈键
  10. JS 音效触发器 / 给动画添加音效