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

news2025/1/18 9:04:36

相关文章:
数据结构–图的概念
图搜索算法 - 深度优先搜索法(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语言实现》

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

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

相关文章

树莓派 4B putty远程连接登录显示拒绝访问,密码修改

putty显示拒绝访问 可能是树莓派的ip没有找到正确的 在下载系统镜像的时候&#xff0c;会提示设置wifi 这里设置的WiFi和密码需记住&#xff0c;主机名也需记住 可以在手机打开热点&#xff08;将热点的账号和密码改为跟你设置的wifi一样的&#xff09; 可以在手机后台查看…

Linux系统的第六天

昨天&#xff0c;学习了vim编辑工具&#xff0c;今天学习Linux系统的目录结构、补充命令和配置网络。 一、目录结果 1.1目录的特点 Windows和Linux&#xff1a; Windows中c、d、e盘&#xff0c;每个都是一个根系统【多根系统】&#xff1b;Linux中只有一个根【单根系统…

Java数据类型:基本数据类型

Java是一种强类型语言&#xff0c;定义变量时&#xff0c;必须指定数据类型。 // 变量必须指定数据类型 private String username;初学者不免有个疑问&#xff1a;在实际编写代码的过程中&#xff0c;该如何选择数据类型呢&#xff1f; 回答这个问题之前&#xff0c;先来解决…

vulhub靶机struts2环境下的s2-032(CVE-2016-3081)(远程命令执行漏洞)

影响范围 Struts 2.3.19至2.3.20.2、2.3.21至2.3.24.1和2.3.25至2.3.28 当用户提交表单数据并验证失败时&#xff0c;后端会将用户之前提交的参数值使用OGNL表达式%{value}进行解析&#xff0c;然后重新填充到对应的表单数据中。 漏洞搭建 没有特殊要求&#xff0c;请看 (3…

给定两点所能得到的数学关系

给定两点所能得到的数学关系 正文 正文 这里介绍一个基础问题&#xff0c;如果给定平面上的两个点的坐标&#xff0c;那么它们之间能够得到什么数学关系呢&#xff1f; ω arctan ⁡ y 1 − y 0 x 1 − x 0 x 1 − x 0 d cos ⁡ ω y 1 − y 0 d cos ⁡ ω d ( x 1 − x…

干部谈话考察:精准洞悉,助推成长

在组织人事管理的精细布局中&#xff0c;干部谈话考察扮演着举足轻重的角色。它不仅是组织深度了解干部、精准评价其表现的重要窗口&#xff0c;更是推动干部个人成长、优化组织人才配置的关键一环。通过深入的谈话考察&#xff0c;我们能够全面把握干部的思想脉搏、工作能力、…

AngularJS指令

指令分类&#xff1a; 1&#xff09;装饰器型指令 装饰器指令的作用是为DOM添加行为&#xff0c;使其具有某种能力。在AngularS中&#xff0c;大多数内置指令属于装饰器型指令&#xff0c;例如ng-click(单击事件)、ng-hide/ng-show(控制DOM元素的显示和隐藏)等 2&#xff09;组…

uniapp 生成安卓证书没有md5指纹怎么办?

由于最新的jdk版本对应的keystore工具无法查看到md5指纹信息 但是不代表它没有md5指纹信息&#xff0c;只是看不到而已 解决方案&#xff1a; 登录uniapp开发者后台生成安卓云端证书

SSM【Spring SpringMVC Mybatis】—— Spring(二)

如果对于Spring的一些基础理论感兴趣可见&#x1f447; SSM【Spring SpringMVC Mybatis】—— Spring&#xff08;一&#xff09; 目录 1、Spring中bean的作用域 1.1 语法 1.2 四个作用域 2、Spring中bean的生命周期 2.1 bean的生命周期 2.2 bean的后置处理器 2.3 添加后…

【Vue】Vue指令与生命周期以及组件化编码

目录 常用内置指令v-text与v-htmlv-text : 更新元素的 textContentv-html : 更新元素的 innerHTML注意&#xff1a;v-html有安全性问题&#xff01;&#xff01;&#xff01;&#xff01; v-once与v-prev-oncev-pre ref与v-cloakrefv-cloak 自定义指令案例定义语法配置对象中常…

一键批量合并视频:掌握视频剪辑技巧解析,轻松创作完美影片

在数字时代的浪潮下&#xff0c;视频已成为人们记录和分享生活的重要工具。然而&#xff0c;对于许多非专业视频编辑者来说&#xff0c;将多个视频片段合并成一个完整的影片却是一项复杂且耗时的任务。幸运的是&#xff0c;云炫AI智剪一键批量合并视频功能的出现&#xff0c;让…

QT切换控件布局

1、切换前垂直布局 2、切换后水平布局 3、关键代码 qDebug() << "开始切换布局";QWidget *widget centralWidget();QLayout *layout widget->layout();if(layout){while(layout->count()){QLayoutItem *item layout->takeAt(0);if(item->layout…

自动化神器Autolt,让你不再重复工作!

随着互联网不断发展&#xff0c;它给我们带来便利的同时&#xff0c;也带来了枯燥、重复、机械的重复工作。今天&#xff0c;我要和大家分享一款老牌实用的自动化工具&#xff1a;AutoIt&#xff0c;它能够让你告别繁琐的重复性工作&#xff0c;提高工作效率。 这里透露一下&am…

C++中的complex

在 C 中&#xff0c;std::complex 是一个模板类&#xff0c;用于表示和操作复数。这个类是标准模板库&#xff08;STL&#xff09;的一部分&#xff0c;包含在 头文件中。std::complex 提供了一套丰富的功能&#xff0c;包括基本的算术运算、比较运算、数学函数等&#xff0c;使…

大语言模型LLM原理篇

大模型席卷全球&#xff0c;彷佛得模型者得天下。对于IT行业来说&#xff0c;以后可能没有各种软件了&#xff0c;只有各种各样的智体&#xff08;Agent&#xff09;调用各种各样的API。在这种大势下&#xff0c;笔者也阅读了很多大模型相关的资料&#xff0c;和很多新手一样&a…

苹果 iPhone 15 Pro Max 称霸:智能手机市场势不可挡

苹果 iPhone 15 Pro Max 称霸&#xff1a;智能手机市场势不可挡 概述 在拥挤且竞争激烈的智能手机市场中&#xff0c;苹果的 iPhone 15 Pro Max 成为明显的赢家&#xff0c;在 2024 年第一季度最畅销智能手机排行榜上名列前茅。根据 Counterpoint Research 的数据&#xff0c…

css: 动态设置网格线

参考这个博客做了网格线&#xff1a; http://t.csdnimg.cn/y20vM 把网格颜色&#xff0c;宽高和透明度做成可配置项。 <e-collapse title"网格线" :expand"false"><t-form-item label"颜色"><el-color-picker v-model"fo…

vue2+Ts中openLayer绘图工具组件封装

vue2Ts中openLayer绘图工具组件封装 效果&#xff1a; 封装组件代码&#xff1a; <!-- openLayer绘图工具 --> <template><a-button-group v-show"isShow"><a-button v-if"shouldShowButton(point)" click"draw(Point)"…

Axure10_win安装教程(安装、汉化、授权码,去弹窗)

1.下载Axure10 链接&#xff1a;https://pan.baidu.com/s/1fc8Bgyic8Ct__1IOv-afUg 提取码&#xff1a;9qew 2.安装Axure10 因为我的电脑是Windows操作系统&#xff0c;所以我下载的AxureRP-Setup-Beta v10.0.0.3816 (Win).exe 一直点下一步就行 3.Axure10中文 打开Axure…

(python)cryptography-安全的加密

前言 cryptography 是一个广泛使用的 Python 加密库&#xff0c;提供了各种加密、哈希和签名算法的实现。它支持多种加密算法&#xff0c;如 AES、RSA、ECC 等&#xff0c;以及哈希函数&#xff08;如 SHA-256、SHA-384 等&#xff09;和数字签名算法(如 DSA、ECDSA 等). 目录 …