图搜索算法-最短路径算法-贝尔曼-福特算法

相关文章:
数据结构–图的概念
图搜索算法 - 深度优先搜索法(DFS)
图搜索算法 - 广度优先搜索法(BFS)
图搜索算法 - 拓扑排序
图搜索算法-最短路径算法-戴克斯特拉算法

贝尔曼-福特算法(Bellman-Ford)

现在继续学习求解最短路径的第二种算法,首先回想戴克斯特拉算法的例子,每个边的权重都是正整数,若出现负权重,算法能不能正常运行呢?看以下例子,如图所示。
在这里插入图片描述

# 运行戴克斯特拉算法
g = DijkstraGraph(['A','B','C','D']) 
g.graph = [[0, 1, 2, 10], 
        [1, 0, 1, -20], 
        [2, 1, 0, 0], 
        [10, -20, 0, 0],
        ]; 
g.dijkstra(0)# A作为源结点
#-------------结果----------------
结点      距离源结点的距离
A        0
B        1
C        2
D        10

结果明显是错误的,大家清楚知道结点【B】到结点【A】最短距离不是1,它可以通过结点【D】到结点【A】,此时最短距离为-10,而且如果能够循环,每次循环距离还能不断缩小,变成一个死循环。既然戴克斯特拉算法没有办法计算负权重问题,那有什么算法可以呢?所以第二个算法正是能够处理此类问题,它叫贝尔曼-福特(Bellman–Ford)算法。

贝尔曼-福特算法最大特点是支持负权重的情况,它每次都会从源结点重新出发对每一个结点进行距离计算并更新最小距离,而戴克斯特拉算法是从源结点出发向外扩逐个处理相邻的结点,不会去重复处理结点,从这也可以知道戴克斯特拉算法相对更高效一点。现在通过一个简单例子,来观察贝尔曼-福特算法处理过程,例子如下图所示。
在这里插入图片描述
(1)这次要处理的是有权有向图,因此图的表示方式使用邻接列。首先初始化【distance】列表,结点【A】为源结点,它和自身的距离为0,其余结点到源结点的距离为无穷大(∞),那么【distance】值为【0,∞,∞,∞,∞】,如图所示。
在这里插入图片描述
(2)每一轮要计算每一个结点到源结点的距离,一共要经过N-1轮边距离松弛(N为结点数),因为结点到源结点若连通,最多就是N-1条边就可以达到。第一轮计算结点【A】一条边能到达的结点,计算过程如表所示。
在这里插入图片描述
(3)根据上表得知,现在【distance】值为【0,-1,∞,3,∞】。然后到第二轮,计算结点【A】两条边能到达的结点,计算过程如表所示。
在这里插入图片描述
(4)根据上表得知,现在【distance】值为【0,-1,3,1,3】。然后到第三轮,计算结点【A】三条边能到达的结点,计算过程如表所示。
在这里插入图片描述
(5)根据上表得知,现在【distance】值为【0,-1,3,1,1】。然后到第四轮,由于结点数为5,因此这是最后一轮(5-1=4),计算结点【A】四条边能到达的结点,计算过程如表所示。
在这里插入图片描述
(6)根据上表得知,现在【distance】值为【0,-1,3,1,1】,最终结果如图所示。
在这里插入图片描述
从上面运算过程可知贝尔曼-福特算法要遍历每一个结点,每次要对所有边进行松弛操作,所以时间复杂度为O(N*E),N为结点数,E为边数。在空间上只需要【distance】来记录结果,因此空间复杂度为O(N)。

现在要用代码来表现上面的过程,首先这是一个有权有向图,可以创建【GraphPower】类来表示。然后创建【GraphBellmanFord】继承【GraphPower】类可以录入邻接列表,bellman_ford()函数是算法的主程序,其中用一个列表【distance】记录每个结点到源结点的距离,print_result()函数格式化输出结果。

class GraphPower(): 
    """有权图类"""
    def __init__(self, points):
        self.amount = len(points) # 记录结点的总数
        self.points = points      # 记录结点位置和值的关系
        self.graph = []           #  初始化图的邻接列表
    def add_edge(self, u, v, w):
        if u in self.points and v in self.points:
            index_u = self.points.index(u)
            index_v = self.points.index(v)
            self.graph.append([index_u, index_v, w]) # 录入数据
        else:
            print("录入数据有误")

class GraphBellmanFord(GraphPower):
    """贝尔曼-福特(Bellman–Ford)算法"""
    def print_result(self, dist): 
        print("结点到源结点的距离:") 
        for i in range(self.amount): 
            print("{} \t {}".format(self.points[i], dist[i])) 
    def bellman_ford(self, source):
        """主程序贝尔曼福特算法求每个结点到源结点最短路径,并且检查是否有负循环"""
        # 第一步:初始化参数,所有结点到源结点的距离为正无穷大 
        distance= [float("Inf")]*self.amount # float("Inf")为正无穷大
        distance[source] = 0  # 源结点本身距离为0
        # 第二步: 进行N-1次的边松弛,找结点到源结点的最短距离
        for i in range(self.amount - 1):
            for u, v, w in self.graph:
                # distance(a) +weight(ab)) < distance(b) 说明存在有更短路径从a到b。
                if distance[u] != float("Inf") and distance[u] + w < distance[v]: 
                        distance[v] = distance[u] + w # 更新最短路径
        # 第三步: 检查是否存在负循环,在完成这么N-1次松弛后如果还是可以松弛(找到更短路径)的话说明存在负循环
        for u, v, w in self.graph:
            if distance[u] != float("Inf") and distance[u] + w < distance[v]: 
                    print("图包含负循环")
                    return
        self.print_result(distance) # 打印结果

以例子为输入,来测试程序。

g = GraphBellmanFord(['A','B','C','D','E']) # 初始化图,记录结点值
g.add_edge('A','B', -1) # 添加边
g.add_edge('A','D', 3) 
g.add_edge('B','D', 2)
g.add_edge('B','C', 4)
g.add_edge('B','E', 4)
g.add_edge('C','E', -2)
g.add_edge('E','B', 1)
g.add_edge('E','D', 5)
g.bellman_ford(0) # 按结点A为源结点计算结点的最短距离
#-------------------结果------------------------
结点到源结点的距离:
A        0
B        -1
C        3
D        1
E        1

结果与手动过程计算出来的结果是一致的。如果修改结点【C】到结点【E】的值为-8,也就是把第七行改为【g.add_edge(2, 4, -8)】,再运行一次,程序便能检查出负循环。大家可以多尝试其他例子,加深算法的认识。

更多内容

想获取完整代码或更多相关图的算法内容,请查看我的书籍:《数据结构和算法基础Python语言实现》

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

OpenAI春季发布会, GPT-4o引爆科技圈 |千字文全面解读

今天&#xff0c;OpenAI再一次引爆了科技圈。这次的核心亮点无疑是他们的全新模型&#xff1a;GPT-4o&#xff0c;以及基于此模型构建的全新ChatGPT版本。 GPT-4o是什么&#xff1f; OpenAI 最新推出的 GPT-4o&#xff0c;“o”代表“Omni”&#xff0c;这一拉丁词根在英语中常…

CentOS报错: Fontconfig head is null, check your fonts or fonts configuration

错误 解决方案 这个报错的原因时java读取本地字体时发现字体损坏或者缺失&#xff0c;只需要补充一下字体就可以了&#xff0c;解决方法安装FontConfig组件即可&#xff1a; sudo yum install fontconfig

弥合孤岛:克服构建 DevOps 文化的挑战

持续变革正在发生软件开发行业。DevOps 因其对自动化、协作和持续改进的关注而成为优化软件交付并弥合开发和运营团队之间鸿沟的重要方法。然而&#xff0c;过渡到真正的 DevOps 文化并非没有挑战。本文探讨了您在追求 DevOps 时可能面临的障碍并提供了解决方案。 01 了解 Dev…

JINGWHALE 数字认证体系 · 进阶知识库

JINGWHALE 数字认证体系 是 JINGWHALE 数字科学艺术创新中心 的数字认证服务。 ◢◤ 宗旨 致力于数字化知行合一的知识赋能&#xff01; ◥ 数字化人才培养 培养数字化思维&#xff0c;传播数字化知识&#xff0c;赋能各行业数字化。 ◥ 职业人才发展 无缝衔接学校高等…

Databend 开源周报第 144 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend 。 了解 Databend …

有一个21年的前端vue项目,死活安不上依赖

在公司开发的时候遇到的一个很玄幻的问题,这个项目是21年开发的,现在我是24年中途二开增加新功能 这个项目经过多人之手,现在已经出现了问题------项目依赖安不上,我能启动完全是因为在23年的时候写这个项目的时候将依赖费九牛二虎之力下载好后打成了压缩包发给另外一个安不上依…

分析 vs2019 c++ 中的 decltype 与 declval

&#xff08;1&#xff09; decltype 可以让推断其参数的类型。按住 ctrl 点击 decltype &#xff0c;会发现无法查阅 其定义 &#xff1a; &#xff08;2&#xff09; 但 STL 库里咱们可以查阅函数 declval 的 定义&#xff0c;很短&#xff0c;摘抄如下&#xff1a; templat…

PostgreSQL源码安装

文章目录 一、先决条件检查二、源码安装1、获取源代码2、编译安装1.运行 configure2.运行make 3、PostgreSQL的配置4、安装contrib目录下的工具 三、初始化数据库1、创建数据库管理员2、创建数据库实例3、启动和停止数据库4、设置数据库密码 四、PostgreSQL的简单配置1、pg_hba…

Java项目实现报文数据校验注解方式(必输项、值大小)

普通项目 导入校验依赖 <dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId><version>4.1.0.Final</version></dependency><dependency><groupId>javax.validation</…

系统定期执行命令的方法

系统定期执行命令的方法 一、进入超级用户下 执行命令&#xff1a;sudo su 二、添加要执行的命令 例子&#xff1a;每天0点执行一次myapp.sh命令 先后输入&#xff1a;crontab -e、 1、 回车 设置每天0点执行一次myapp.sh操作&#xff0c;需要写绝对路径 含义&#xff1…

RK3576 Camera:资源介绍

RK3576是RK今年上市的中高端旗舰芯片&#xff0c;定位弱于RK3588。这篇文章主要分享一下RK3576这颗主控芯片的camera资源。 &#xff08;1&#xff09;RK3576 camera资源 ①RK3576 camera硬件框图 RK3576的camera硬件框图如图所示&#xff0c;拥有一路4lane的DCPHY&#xff…

Spring Cloud Consul 4.1.1

该项目通过自动配置和绑定到 Spring 环境和其他 Spring 编程模型习惯用法&#xff0c;为 Spring Boot 应用程序提供 Consul 集成。通过一些简单的注释&#xff0c;您可以快速启用和配置应用程序内的常见模式&#xff0c;并使用基于 Consul 的组件构建大型分布式系统。提供的模式…

银河麒麟v10 重装系统恢复原home分区

现象&#xff1a;系统还原后在锁屏状态下输入密码后闪退回锁屏 ctrl alt f1切到命令行模式&#xff0c;查看/home目录下的用户文件夹里无文件 1、blkid找到data分区的uuid和设备编号&#xff0c;记录下来&#xff1b; 2、sudo mount /dev/sda5 3、sudo vi /etc/fstab&#xf…

JAVA中类和对象(承接上次的补充)

目录&#xff1a; 一.static修饰成员方法 二.static成员变量初始化 三.代码块 一.static修饰成员方法: 1.一般类中的数据成员都设置为 private &#xff0c;而成员方法设置为 public &#xff0c; 问&#xff1a;那设置之后&#xff0c;Student类中&#xff0c;被Student修饰…

数据结构——01-抽奖数人-链表-实验题目与解答

数据结构抽奖数人链表实验题目与解答 一、**实验题目** 抽奖游戏&#xff1a; n个人围成一圈&#xff0c;由第一个人开始&#xff0c;依次报数&#xff0c;数到第m人&#xff0c;便抽出来作为中奖人&#xff0c;然后从他的下一个人数起&#xff0c;数到第m人&#xff0c;再抽…

VALSE 2024合合信息 | 文档解析与向量化技术加速多模态大模型训练与应用

第十四届视觉与学习青年学者研讨会&#xff08;VALSE 2024&#xff09;近期在重庆悦来国际会议中心圆满举行&#xff0c;由中国人工智能学会&#xff08;CAAI&#xff09;、中国图象图形学会&#xff08;CSIG&#xff09;、中国民族贸易促进会主办&#xff0c;重庆邮电大学承办…

goconvey测试框架的使用

尽管Golang已经内置了功能强大的testing包&#xff0c;其易用性令人称赞。然而&#xff0c;当我们希望更直观地处理和判断测试结果时&#xff0c;结合使用goconvey能为我们提供极大的便利。goconvey不仅为我们提供了丰富的断言函数&#xff0c;这些函数还极大地方便了我们在进行…

Web测试是在测什么?容易被忽视的小细节总结!

随着Internet和Intranet/Extranet的快速增长&#xff0c;Web已经对商业、工业、银行、财政、教育、政府和娱乐及我们的工作和生活产生了深远的影响。许多传统的信息和数据库系统正在被移植到互联网上&#xff0c;电子商务迅速增长&#xff0c;早已超过了国界。范围广泛的、复杂…

C# XPTable in .net6(XPTable控件使用说明八)

经过作者schoetbi、armin-pfaeffle的努力&#xff0c;XPTable已经可以在 winform .net6 .net8的环境下使用&#xff0c;版本升级到了2.0&#xff0c;这样就可以在winform下同时使用XPTABLE和EFcore, 这样就可以解决大部分的场景了。

网络工程师----第二十八天

计算机基础 第五章&#xff1a;运输层 运输层的两个协议&#xff1a; 1、传输控制协议TCP&#xff1a; TCP最主要的特点&#xff1a; (1)TCP是面向连接的。应用程序在使用TCP协议之前&#xff0c;必须先建立连接。在传送数据完毕后&#xff0c;必须释放已经建立的TCP连接。…