【Algorithms 4】算法(第4版)学习笔记 18 - 4.4 最短路径

news2024/11/18 18:42:13

文章目录

    • 前言
    • 参考目录
    • 学习笔记
      • 0:引入介绍
      • 1:APIs
      • 1.1:API:加权有向边
      • 1.2:Java 实现:加权有向边
      • 1.3:API:加权有向图
      • 1.4:Java 实现:加权有向图
      • 1.5:API:最短路径
      • 2:最短路径性质
      • 2.1:最短路径的数据结构
      • 2.2:边的松弛 edge relaxation
      • 2.3:最优性条件 optimality conditions
      • 2.4:通用算法 generic shortest-paths algorithm
      • 3:Dijkstra 算法
      • 3.1:demo 演示
      • 3.2:证明
      • 3.3:Java 实现
      • 3.4:计算图生成树
      • 3.5:运行时间
      • 4:无环加权有向图 edge-weighted DAGs
      • 4.1:demo 演示
      • 4.2:证明
      • 4.3:Java 实现
      • 4.4:最长路径
      • 5:负权重 negative weights
      • 5.1:失败尝试
      • 5.2:负权重环 negative cycles
      • 5.3:Bellman-Ford 算法
      • 5.3.1:demo 演示
      • 5.3.2:算法分析
      • 5.4:成本开销小结
      • 6:小结

前言

本篇主要内容包括:APIs最短路径性质Dijkstra 算法无环加权有向图 以及 负权重

参考目录

  • B站 普林斯顿大学《Algorithms》视频课
    (请自行搜索。主要以该视频课顺序来进行笔记整理,课程讲述的教授本人是该书原版作者之一 Robert Sedgewick。)
  • 微信读书《算法(第4版)》
    (本文主要内容来自《4.4 最短路径》)
  • 官方网站
    (有书本配套的内容以及代码)

学习笔记

注1:下面引用内容如无注明出处,均是书中摘录。
注2:所有 demo 演示均为视频 PPT demo 截图。
注3:如果 PPT 截图中没有翻译,会在下面进行汉化翻译,因为内容比较多,本文不再一一说明。

0:引入介绍

加权有向图的最短路径(shortest paths in an edge-weighted digraph):

![image-20240315082159395]

最短路径的应用:

![L15-44ShortestPaths_03]在这里插入图片描述

  • PERT/CPM:计划评审技术/关键路径法
  • 地图路线规划
  • 接缝雕刻(图像缩放中的内容感知算法)
  • 纹理映射(计算机图形学中将图像纹理贴到三维模型表面的技术)
  • 机器人导航技术
  • TeX 中的排版设置
  • 城市交通规划
  • VLSI 芯片的最佳流水线设计
  • 电话营销员操作员调度问题
  • 电信消息的路由选择
  • 网络路由协议(如 OSPF、BGP、RIP)
  • 利用货币兑换市场中的套利机会
  • 根据既定交通拥堵模式确定卡车最优行驶路线

Shortest path is a really interesting and important problem solving model. There’s all kinds of important practical problems that can be recast as shortest paths problems. And because we have efficient solutions to the shortest path, efficient algorithms for finding shortest paths, we have efficient solutions to all these kinds of problems.

最短路径是一个非常有趣且重要的问题解决模型。许多重要的实际问题都可以被重新表述为最短路径问题。正因为如此,我们拥有寻找最短路径的有效解决方案,即高效的最短路径算法,因此也就拥有了解决这类各种问题的有效方法。

最短路径的不同类型:

![L15-44ShortestPaths_04]

顶点类型:

  • 单源:从一个顶点 s 到其他所有顶点。
  • 单汇点:从每个顶点到另一个特定顶点 t。
  • 源汇点:从一个顶点 s 到另一个顶点 t。
  • 全对全:在所有顶点对之间。

边权重限制:

  • 非负权重:所有边的权重都是非负数。
  • 欧几里得权重:边权重基于欧几里得距离。
  • 任意权重:边的权重可以是任意数值。

循环条件:

  • 无有向循环:图中不存在有向循环。
  • 无负权循环:图中不存在权重总和为负值的循环。

简化假设: 从顶点 s 到每个顶点 v 都存在最短路径。

1:APIs

1.1:API:加权有向边

![image-20240315085348075]

1.2:Java 实现:加权有向边

edu.princeton.cs.algs4.DirectedEdge

![image-20240315085631710]

![image-20240315085644190]

1.3:API:加权有向图

![image-20240315085915603]

![image-20240315090301981]

![image-20240315090431816]

1.4:Java 实现:加权有向图

edu.princeton.cs.algs4.EdgeWeightedDigraph

![image-20240315092205110]

![image-20240315092241700]

1.5:API:最短路径

![image-20240315092512628]

2:最短路径性质

2.1:最短路径的数据结构

![L15-44ShortestPaths_14]

目标: 找到从顶点 s 到其他所有顶点的最短路径。

观察结论: 存在一种最短路径树(Shortest Paths Tree, SPT)解法。为什么?

结果: 可以通过两个以顶点索引为键的数组来表示 SPT:

  • distTo[v] 表示从顶点 s 到顶点 v 的最短路径长度。
  • edgeTo[v] 表示从顶点 s 到顶点 v 的最短路径上的最后一段边。

![image-20240315094302501]

对应书本的介绍:

![image-20240315093903240]

2.2:边的松弛 edge relaxation

![L15-44ShortestPaths_16]

对边 e = v → w 进行松弛操作时:

  • distTo[v] 保存已知从源点 s 到顶点 v 的最短路径长度。
  • distTo[w] 保存已知从源点 s 到顶点 w 的最短路径长度。
  • edgeTo[w] 记录已知从源点 s 到顶点 w 的最短路径上最后经过的边。
  • 若通过边 e = v → w 能够到达顶点 w,并且这条路径比之前已知的从 s 到 w 的最短路径更短,则更新 distTo[w] 以及 edgeTo[w]

![image-20240315095849401]

2.3:最优性条件 optimality conditions

![image-20240315100606867]

命题: 设G是一个带权有向图(edge-weighted digraph),则 distTo[] 数组存储的是从源点 s 到各顶点的最短路径距离,当且仅当满足以下条件:

  • distTo[s] = 0,即源点s到自身的最短路径距离为0。
  • 对于每个顶点 v,distTo[v] 表示从源点 s 到顶点 v 的某条路径的长度。
  • 对于每条边 e = v → wdistTo[w] 的值小于等于从源点 s 经由顶点 v 再到顶点 w 的路径长度,即 distTo[w] ≤ distTo[v] + e.weight(),其中 e.weight() 表示边 v → w 的权重。

对应书本命题 P:

![image-20240315100652432]

必要性证明:

![image-20240315101018144]

对应书本的证明:

![image-20240315101424511]

充分性证明:

![image-20240315101504323]

对应书本的证明:

![image-20240315101559185]

2.4:通用算法 generic shortest-paths algorithm

对应书本命题 Q:

![image-20240315102227890]

3:Dijkstra 算法

3.1:demo 演示

![image-20240315103616581]

  • 按照离源点 s 的距离递增顺序考虑顶点(选取具有最低 distTo[] 值的非树顶点)。
  • 将该顶点添加至树结构中,并对其指向的所有边执行松弛操作。

初始状态:

![image-20240315103812262]

距离 s 最近是顶点 0,从顶点 0 开始:

![image-20240315103957761]

对从顶点 0 开始的边进行松弛操作:

![image-20240315104159042]

顶点 0 到 1 为最短路径,继续选择顶点 1:

![image-20240315110732282]

对从顶点 1 开始的边进行松弛操作:

![image-20240315110937592]

![image-20240315111120815]

顶点 0 到 7 为最短路径,继续选择顶点 7:

![image-20240315112458304]

对从顶点 7 开始的边进行松弛操作:

![image-20240315112525900]

![image-20240315112605879]

顶点 0 到 4 为最短路径,继续选择顶点 4:

![image-20240315112740572]

对从顶点 4 开始的边进行松弛操作:

![image-20240315112842144]

![image-20240315112904984]

顶点 4 到 5 为最短路径,继续选择顶点 5:

![image-20240315113029552]

对从顶点 5 开始的边进行松弛操作:

![image-20240315113110077]

![image-20240315113126024]

顶点 5 到 2 为最短路径,继续选择顶点 2:

![image-20240315113207365]

对从顶点 2 开始的边进行松弛操作:

![image-20240315113236950]

![image-20240315113249721]

顶点 2 到 3 为最短路径,继续选择顶点 3:

![image-20240315113329032]

对从顶点 3 开始的边进行松弛操作:

![image-20240315113348502]

![image-20240315113406022]

顶点 2 到 6 为最短路径,继续选择顶点 6:

![image-20240315113446235]

对从顶点 6 开始的边进行松弛操作:

![image-20240315113534275]

s 开始的最短路径树:

![image-20240315113948791]

3.2:证明

![L15-44ShortestPaths_29]

对应书本命题 R:

![image-20240315114148251]

3.3:Java 实现

edu.princeton.cs.algs4.DijkstraSP

![image-20240315115804455]

![image-20240315115855899]

edu.princeton.cs.algs4.DijkstraSP#relax

![image-20240315120026331]

3.4:计算图生成树

![L15-44ShortestPaths_33]

Dijkstra 算法应该相当熟悉吧?

  • Prim 算法在本质上是相同的算法。
  • 两者都属于计算生成树的一类算法。

主要区别在于 选择下一个加入树中的顶点时所依据的规则:

  • Prim 算法选择的是距离当前生成树最近的顶点(通过无向边);
  • 而 Dijkstra 算法选择的是距离源点最近的顶点(通过有向路径)。

注: 深度优先搜索(DFS)和广度优先搜索(BFS)也属于这一类用于生成树或遍历图的算法。

3.5:运行时间

(同 Prim 算法)

![L15-44ShortestPaths_32]

核心要点:

  • 采用数组实现对稠密图(Dense graphs)而言是最佳方案。
  • 对于稀疏图(Sparse graphs),二叉堆在性能上要快得多。
  • 在对性能要求极高的情况下,使用四路堆(4-way heap)是值得投入精力提升性能的。
  • 斐波那契堆在理论上的优越性虽高,但在实际开发中却未必值得进行具体实现。

4:无环加权有向图 edge-weighted DAGs

4.1:demo 演示

![image-20240316134718054]

  • 按拓扑排序考虑顶点。
  • 从该顶点出发对所有指向的边进行松弛操作。

初始状态:

![image-20240316134854048]

首先对顶点进行拓扑排序:

![image-20240316135043894]

从顶点 0 开始,对从顶点 0 开始的边进行松弛操作:

![image-20240316135324509]

继续选择顶点 1,并进行松弛操作:

![image-20240316135515915]

继续选择顶点 4,并进行松弛操作:

![image-20240316141338821]

继续选择顶点 7,并进行松弛操作:

![image-20240316141608117]

继续选择顶点 5,并进行松弛操作:

![image-20240316141644511]

继续选择顶点 2,并进行松弛操作:

![image-20240316141819644]

继续选择顶点 3,并进行松弛操作:

![image-20240316143812156]

继续选择顶点 6,并进行松弛操作:

![image-20240316143839873]

s 开始的最短路径树:

![image-20240316144501557]

4.2:证明

![L15-44ShortestPaths_38]

对应书本命题 S:

![image-20240316145708486]

4.3:Java 实现

edu.princeton.cs.algs4.AcyclicSP

![image-20240316151408099]

![image-20240316151431446]

4.4:最长路径

![L15-44ShortestPaths_46]

带权重有向无环图(DAG)中的最短路径问题:

  • 对所有边的权重取反。
  • 找出这些取反权重后的最短路径。
  • 在得到的结果中再次对边的权重取反。

(等价于:在 relax() 函数中反转相等性判断的方向)

关键点: 拓扑排序算法即使在存在负权边的情况下也能正常工作。

5:负权重 negative weights

5.1:失败尝试

![L15-44ShortestPaths_51]

Dijkstra 算法: 该算法无法处理具有负权重的边。
重赋权重法: 对所有边权重增加一个常数值的方法无效。
结论: 我们需要采用一种不同的算法来解决此问题。

5.2:负权重环 negative cycles

![L15-44ShortestPaths_52]

对应书本定义以及命题 W:

![image-20240316161032461]

![image-20240316161058157]

5.3:Bellman-Ford 算法

![L15-44ShortestPaths_53]

对应书本命题 X:

![image-20240316163337743]

5.3.1:demo 演示

![image-20240316163457806]

重复 V 次:松弛有向图 E 所有边。

初始状态:

![image-20240316163628062]

初始化,将到源的距离设置为 0:

![image-20240316163838365]

对顶点 0 所有有向边进行松弛操作:

![image-20240316164534404]

![image-20240316164759986]

![image-20240316164813178]

对顶点 1 所有有向边进行松弛操作:

![image-20240316164925311]

![image-20240316164943422]

![image-20240316165014037]

对顶点 2 所有有向边进行松弛操作:

![image-20240316165052683]

![image-20240316165128407]

对顶点 3 所有有向边进行松弛操作:

![image-20240316165152695]

对顶点 4 所有有向边进行松弛操作:

![image-20240316165252427]

![image-20240316165320702]

![image-20240316165349289]

对顶点 5 所有有向边进行松弛操作:

![image-20240316165439474]

![image-20240316165504694]

对顶点 7 所有有向边进行松弛操作:

![image-20240316165531693]

![image-20240316165552335]

以上完成第一轮操作。

再次进行松弛操作。

大部分操作与第一轮类似,但也有可以更新的部分:

![image-20240316165907917]

![image-20240316170050428]

同理进行后续的循环:

![image-20240316170146032]

没有更加优化的路径,完成所有松弛操作。

最终得到 s 开始的最短路径树:

![image-20240316170308062]

5.3.2:算法分析

![L15-44ShortestPaths_57]

对应书本命题 Y:

![image-20240316181217238]

5.4:成本开销小结

![L15-44ShortestPaths_59]

  • 注释1: 存在有向循环会使问题求解难度增大。
  • 注释2: 负权重边的存在会使问题变得更加复杂。
  • 注释3: 负权重环会导致问题无法有效解决或无解状态。

6:小结

![L15-44ShortestPaths_65]

非负权重:

  • 在许多应用中出现。
  • Dijkstra 算法的时间复杂度接近线性时间。

无环带权有向图:

  • 在某些应用中出现。
  • 通过拓扑排序算法可以在线性时间内求解。
  • 边的权重可以为负。

负权重和负权重环:

  • 在某些应用中出现。
  • 如果不存在负权重环,可以通过 Bellman-Ford 算法找到最短路径。
  • 若存在负权重环,仍可通过 Bellman-Ford 算法找到一条路径(但可能不是最短路径)。

最短路径问题是广泛应用于问题求解的一个模型。

![image-20240316183053632]

(完)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1521534.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NVidia NX 中 ROS serial软件包的安装

自己装的ROS是noetic版本,受限于网络,直接用命令安装串口包不行。于是手动安装了一次。 1 下载源码 git clone https://github.com/wjwwood/serial.git 或者直接在浏览器里面输入 https://github.com/wjwwood/serial.git 2 解压 然后在serial&#xf…

【考研数学】高等数学总结

文章目录 第一章 极限 函数 连续1.1 极限存在准则及两个重要极限1.1.1 夹逼定理1.1.1.1 数列夹逼定理1.1.1.2函数夹逼定理 1.1.2 两个重要极限1.1.2.1 极限公式11.1.2.1.1 证明1.1.2.1.2 数列的单调有界收敛准则1.1.2.1.2.1 二项式定理1.1.2.1.2.2 证明 1.1.2.2 极限公式21.1.2…

未来洞见:亚信安慧AntDB在数据可靠性上的愿景

和国外成熟稳定的商业数据库相比,国产数据库在性能、稳定性、生态等方面存在一定差距,我国数据库的自主可控替换,也不是简单的以库换库,而是用新体系替换旧体系,在架构、研发、上线、运维等方面,全面降低对…

Pyqt5中,QGroupBox组件标题字样(标题和内容样式分开设置)相对于解除继承

Python代码示例: import sys from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QGroupBox, QLabelclass MyApp(QWidget):def __init__(self):super().__init__()# 创建一个 QVBoxLayout 实例layout QVBoxLayout()# 创建 QGroupBox 实例self.grou…

系统分析与设计作业 --- 酒店管理系统(2~3周)

第二周 作业一: (1)需求分析NABCD 我们的项目是一个酒店管理系统,所i对应的NABCD描述 NABCD是一种产品描述框架,用于全面阐述产品的各个方面。其中,N代表需求(Need),描…

5_springboot_shiro_jwt_多端认证鉴权_禁用Cookie

1. Cookie是什么 ​ Cookie是一种在客户端(通常是用户的Web浏览器)和服务器之间进行状态管理的技术。当用户访问Web服务器时,服务器可以向用户的浏览器发送一个名为Cookie的小数据块。浏览器会将这个Cookie存储在客户端,为这个Co…

字符串分割(C++)

经常碰到字符串分割的问题,这里总结下,也方便我以后使用。 一、用strtok函数进行字符串分割 原型: char *strtok(char *str, const char *delim); 功能:分解字符串为一组字符串。 参数说明:str为要分解的字符串&am…

LeetCode每日一题 将有序数组转换为二叉搜索树(分治)

题目描述 给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵平衡二叉搜索树。 示例 1: 输入:nums [-10,-3,0,5,9] 输出:[0,-3,9,-10,null,5] 解释:[0,-10,5,null,-3,null,9] 也将被视…

[RAM] RAM 突发传输(Burst ,Burst size, length) | Burst 读写过程与时序 精讲

主页: 元存储博客 文章目录 前言1. Burst 基本概念含义Burst Width &Burst Length 2. CPU Burst mode3. 总线 burst mode总线的仲裁总线突发传输时序 4. Burst Chop (突发终止)5. Burst Mode 应用什么时候用突发模式 总结 前言 在DMA(直接内存访问&…

MD5算法:密码学中的传奇

title: MD5算法:密码学中的传奇 date: 2024/3/15 20:08:07 updated: 2024/3/15 20:08:07 tags: MD5起源算法原理安全分析优缺点比较技术改进示例代码应用趋势 MD5算法起源: MD5(Message Digest Algorithm 5)算法是由MIT的计算机…

Web框架盘点:好用又实用的技术解析

​随着2024年的临近,我们满怀热情地为新的一年制定计划,探索未来一年可以学习或实现的目标。此时是探索未来一年值得学习的框架、理解其功能和特点的最佳时机。我们将以2023年JavaScript新星为指南,力求保持客观公正的态度。对于每个值得关注…

调皮的String及多种玩法(下部)

👨‍💻作者简介:👨🏻‍🎓告别,今天 📔高质量专栏 :☕java趣味之旅 欢迎🙏点赞🗣️评论📥收藏💓关注 💖衷心的希…

修复 error Delete `␍` prettier/prettier 错误

修复 error Delete ␍ prettier/prettier 错误 问题背景报错信息报错原因解决办法修改CRLF----针对单个文件yarn run lint --fix 一键修复(官方提供) 问题背景 今天在使用 openapi 自动生成前端接口代码的时候,爆了一个类似 eslint 规范的错…

C/C++炸弹人游戏

参考书籍《啊哈,算法》,很有意思的一本算法书,小白也可以看懂,详细见书,这里只提供代码和运行结果。 这里用到的是枚举思想,还有更好地搜索做法。 如果大家有看不懂的地方或提出建议,欢迎评论区…

外包干了9天,技术退步明显。。。。。

先说一下自己的情况,本科生,2018年我通过校招踏入了南京一家软件公司,开始了我的职业生涯。那时的我,满怀热血和憧憬,期待着在这个行业中闯出一片天地。然而,随着时间的推移,我发现自己逐渐陷入…

Django之Cookie

Django之Cookie 目录 Django之Cookie介绍Django操作Cookie设置Cookie浏览器查看Cookie 获取Cookie设置超时Cookie注销Cookie 模拟登录验证登录验证装饰器登录验证装饰器-升级版 介绍 当我们上网使用社交媒体或者购物时,浏览器需要通过一种方式来记住我们。想象一下…

Java宝典-异常

目录 1. 异常的分类1.1 运行时异常1.2 编译时异常 2. 异常的抛出2.1 throw2.2 throws 3. 异常的捕获3.1 try-catch3.2 finally 4. 异常执行的过程5. 自定义异常 在Java中,异常(Exception)是指程序发生不正常的行为,异常其实就是一个一个的类。 1. 异常的…

算法-贪心-122. 糖果传递

题目 有 n个小朋友坐成一圈,每人有 a[i]个糖果。 每人只能给左右两人传递糖果。 每人每次传递一个糖果代价为 1。 求使所有人获得均等糖果的最小代价。 输入格式 第一行输入一个正整数 n,表示小朋友的个数。 接下来 n 行,每行一个整数…

数据结构 之 优先级队列(堆) (PriorityQueue)

🎉欢迎大家观看AUGENSTERN_dc的文章(o゜▽゜)o☆✨✨ 🎉感谢各位读者在百忙之中抽出时间来垂阅我的文章,我会尽我所能向的大家分享我的知识和经验📖 🎉希望我们在一篇篇的文章中能够共同进步!!&…

langchain学习(十二)

Chat Messages | 🦜️🔗 Langchain ChatMessageHistory:基类,保存HumanMessages、AIMessages from langchain.memory import ChatMessageHistory history ChatMessageHistory() history.add_user_message("hi!") his…