最短路径算法:Bellman-Ford算法
引言
在图论中,Bellman-Ford算法是一种用于计算单源最短路径的算法。与Dijkstra算法不同,Bellman-Ford算法可以处理带有负权边的图,并且可以检测图中是否存在负权环。本文将详细介绍Bellman-Ford算法的定义、步骤及其实现。
Bellman-Ford算法
定义
Bellman-Ford算法是一种用于计算从源顶点到图中所有其他顶点的最短路径的算法。该算法可以处理带有负权边的图,并且可以检测是否存在负权环。
算法步骤
- 初始化:设定源顶点的距离为0,其余顶点的距离为正无穷大。
- 松弛操作:对所有边进行(V-1)次松弛操作,其中(V)是顶点的数量。对于每条边(u, v),如果
dist[u] + weight < dist[v],则更新dist[v] = dist[u] + weight。 - 检查负权环:对所有边进行一次检查,如果发现仍然可以进行松弛操作,则说明图中存在负权环。
示例
假设我们有一个带权有向图,顶点集合为 ({A, B, C, D, E}),边和权重集合为 ({(A, B, -1), (A, C, 4), (B, C, 3), (B, D, 2), (B, E, 2), (D, B, 1), (D, C, 5), (E, D, -3)})。
Bellman-Ford算法图解
步骤1:初始化
将源顶点A的距离设为0,其余顶点的距离设为正无穷大。
顶点: A B C D E
距离: 0 ∞ ∞ ∞ ∞
步骤2:第一次松弛操作
对每条边进行松弛操作:
- 对于边 (A, B, -1):更新 B 的距离为 -1。
- 对于边 (A, C, 4):更新 C 的距离为 4。
- 对于边 (B, C, 3):更新 C 的距离为 2。
- 对于边 (B, D, 2):更新 D 的距离为 1。
- 对于边 (B, E, 2):更新 E 的距离为 1。
- 对于边 (D, B, 1):不更新 B 的距离。
- 对于边 (D, C, 5):不更新 C 的距离。
- 对于边 (E, D, -3):更新 D 的距离为 -2。
顶点: A B C D E
距离: 0 -1 2 -2 1
步骤3:第二次松弛操作
对每条边再次进行松弛操作:
- 对于边 (A, B, -1):不更新 B 的距离。
- 对于边 (A, C, 4):不更新 C 的距离。
- 对于边 (B, C, 3):不更新 C 的距离。
- 对于边 (B, D, 2):不更新 D 的距离。
- 对于边 (B, E, 2):不更新 E 的距离。
- 对于边 (D, B, 1):不更新 B 的距离。
- 对于边 (D, C, 5):不更新 C 的距离。
- 对于边 (E, D, -3):不更新 D 的距离。
顶点: A B C D E
距离: 0 -1 2 -2 1
步骤4:检查负权环
对每条边进行一次检查,如果发现仍然可以进行松弛操作,则说明图中存在负权环。在此示例中,没有发现负权环。
Bellman-Ford算法实现
下面是用Java实现Bellman-Ford算法的代码示例:
import java.util.Arrays;public class BellmanFordAlgorithm {private int vertices; // 顶点数量private int[][] edges; // 边数组,包含边的起点、终点和权重private int edgeCount; // 边数量public BellmanFordAlgorithm(int vertices, int edgeCount) {this.vertices = vertices;this.edgeCount = edgeCount;edges = new int[edgeCount][3];}// 添加边public void addEdge(int edgeIndex, int src, int dest, int weight) {edges[edgeIndex][0] = src;edges[edgeIndex][1] = dest;edges[edgeIndex][2] = weight;}// 计算从源顶点到所有顶点的最短路径public void bellmanFord(int src) {int[] dist = new int[vertices]; // 最短距离数组Arrays.fill(dist, Integer.MAX_VALUE);dist[src] = 0;// 对所有边进行 V-1 次松弛操作for (int i = 1; i < vertices; i++) {for (int j = 0; j < edgeCount; j++) {int u = edges[j][0];int v = edges[j][1];int weight = edges[j][2];if (dist[u] != Integer.MAX_VALUE && dist[u] + weight < dist[v]) {dist[v] = dist[u] + weight;}}}// 检查是否存在负权环for (int j = 0; j < edgeCount; j++) {int u = edges[j][0];int v = edges[j][1];int weight = edges[j][2];if (dist[u] != Integer.MAX_VALUE && dist[u] + weight < dist[v]) {System.out.println("图中存在负权环");return;}}printSolution(dist);}// 打印最短路径private void printSolution(int[] dist) {System.out.println("顶点\t距离源顶点");for (int i = 0; i < vertices; i++) {System.out.println(i + "\t\t" + dist[i]);}}public static void main(String[] args) {int vertices = 5;int edgeCount = 8;BellmanFordAlgorithm graph = new BellmanFordAlgorithm(vertices, edgeCount);graph.addEdge(0, 0, 1, -1);graph.addEdge(1, 0, 2, 4);graph.addEdge(2, 1, 2, 3);graph.addEdge(3, 1, 3, 2);graph.addEdge(4, 1, 4, 2);graph.addEdge(5, 3, 1, 1);graph.addEdge(6, 3, 2, 5);graph.addEdge(7, 4, 3, -3);graph.bellmanFord(0); // 从顶点0开始计算最短路径}
}
代码注释
-
类和构造函数:
public class BellmanFordAlgorithm {private int vertices; // 顶点数量private int[][] edges; // 边数组,包含边的起点、终点和权重private int edgeCount; // 边数量public BellmanFordAlgorithm(int vertices, int edgeCount) {this.vertices = vertices;this.edgeCount = edgeCount;edges = new int[edgeCount][3];}BellmanFordAlgorithm类包含图的顶点数量和边数组,并有一个构造函数来初始化这些变量。 -
添加边:
public void addEdge(int edgeIndex, int src, int dest, int weight) {edges[edgeIndex][0] = src;edges[edgeIndex][1] = dest;edges[edgeIndex][2] = weight; }addEdge方法用于向图中添加边。 -
Bellman-Ford算法:
public void bellmanFord(int src) {int[] dist = new int[vertices]; // 最短距离数组Arrays.fill(dist, Integer.MAX_VALUE);dist[src] = 0;// 对所有边进行 V-1 次松弛操作for (int i = 1; i < vertices; i++) {for (int j = 0; j < edgeCount; j++) {int u = edges[j][0];int v = edges[j][1];int weight = edges[j][2];if (dist[u] != Integer.MAX_VALUE && dist[u] + weight < dist[v]) {dist[v] = dist[u] + weight;}}}// 检查是否存在负权环for (int j = 0; j < edgeCount; j++) {int u = edges[j][0];int v = edges[j][1];int weight = edges[j][2];if (dist[u] != Integer.MAX_VALUE && dist[u] + weight < dist[v]) {System.out.println("图中存在负权环");return;}}printSolution(dist); }bellmanFord方法实现了Bellman-Ford算法,计算从源顶点到所有其他顶点的最短路径,并检测是否存在负权环。 -
打印最短路径:
private void printSolution(int[] dist) {System.out.println("顶点\t距离源顶点");for (int i = 0; i < vertices; i++) {System.out.println(i + "\t\t" + dist[i]);} }printSolution方法用于打印最短路径。
结论
通过上述讲解和实例代码,我们详细展示了Bellman-Ford算法的定义、步骤及其实现。Bellman-Ford算法是一种重要的最短路径算法,特别适用于带有负权边的图,并且可以检测负权环。希望这篇博客对您有所帮助!
如果您觉得这篇文章对您有帮助,请关注我的CSDN博客,点赞并收藏这篇文章,您的支持是我持续创作的动力!
关键内容总结:
- Bellman-Ford算法的定义
- Bellman-Ford算法的步骤
- Bellman-Ford算法的实现及其代码注释
推荐阅读:深入探索设计模式专栏,详细讲解各种设计模式的应用和优化。点击查看:深入探索设计模式。
特别推荐:设计模式实战专栏,深入解析设计模式的实际应用,提升您的编程技巧。点击查看:设计模式实战。
如有任何疑问或建议,欢迎在评论区留言讨论。谢谢阅读!
相关文章:
最短路径算法:Bellman-Ford算法
引言 在图论中,Bellman-Ford算法是一种用于计算单源最短路径的算法。与Dijkstra算法不同,Bellman-Ford算法可以处理带有负权边的图,并且可以检测图中是否存在负权环。本文将详细介绍Bellman-Ford算法的定义、步骤及其实现。 Bellman-Ford算…...
爬虫:xpath模块及昵图网实例
xpath模块 from lxml import etreestr1 """ <div><ul><li class"item-0"><a href"link1.html">first item</a></li><li class"item-1"><a href"link2.html">second…...
高级java每日一道面试题-2024年8月03日-web篇-forward和redirect有什么区别?
如果有遗漏,评论区告诉我进行补充 面试官: forward和redirect有什么区别? 我回答: 在Java Web开发中,forward和redirect是Servlet容器提供的两种用于页面跳转的技术。它们的主要区别在于客户端感知的方式、URL地址的变化、请求对象的共享等方面。下面详细介绍两…...
如何让你的网站拥有更好的体验
在HTML中,属性是用于提供关于HTML元素的额外信息。接下来我们将讲解13个可以让用户拥有更好体验的HTML属性。 Accept 属性 我们可以在<input>元素(仅适用于文件类型)中使用accept属性来指定服务器可以接受的文件类型。 <input ty…...
opencascade AIS_TypeFilter AIS_XRTrackedDevice源码学习
opencascade AIS_TypeFilter 前言 通过它们的类型选择交互对象。该过滤器会对本地上下文中的每个交互对象提出问题, 以确定它是否具有非空的所有者,并且如果是,则检查它是否是所需类型。 如果对象在每种情况下都返回 true,则保留…...
使用Spring AOP监控指定方法执行时间
文章目录 一、加入pom依赖二、切面类和注解三、执行方法 一、加入pom依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>二、切面类和注解 import java.lang.…...
最新CSS3纵向菜单的实现
纵向菜单 通过下面例子,你会知道把列表转换成菜单的关键技术 a中的#是URL的占位符可以点击,真正用途中写实际URL <nav class"list1"><ul><li><a href"#">Alternative</a></li><li><…...
GooLeNet模型搭建
一、model import torch from torch import nn from torchsummary import summaryclass Inception(nn.Module):def __init__(self, in_channels, c1, c2 , c3 , c4):super(Inception, self).__init__()self.ReLU nn.ReLU()#路线1:1x1卷积self.p1_1 nn.Conv2d(in_channels i…...
使用ThreadLocal来存取单线程内的数据
一.什么是ThreadLocal? ThreadLocal,即线程本地变量。如果你创建了一个 ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个本地拷贝,多个线程操作这个变量的时候,实际是在操作自己本地内存里面的变量&…...
elasticsearch教程
1. 单点部署(rpm): #提前关闭firewalld,否则无法组建集群 #1. 下载ES rpm包 ]# https://www.elastic.co/cn/downloads #2. 安装es ]# rpm -ivh elasticsearch-7.17.5-x86_64.rpm #3. 调整内核参数(太低的话es会启动报错) echo "vm.max_map_count655360 fs.file-max 655…...
Arrays、Lambda表达式、Collection集合
1. Arrays 1.1 操作数组的工具类 方法名说明public static String toString(数组)把数组拼接成一个字符串public static int binarySearch(数组,查找的元素)二分查找法查找元素public static int[] copyOf(原数组,新数组长度)拷贝数组public static int[] copyOfRange(原数组…...
2024年前端趋势:全栈或许是不容错过的选择!
近年来,前端开发的技术不断推陈出新,2024年也不例外。在这个变化迅速的领域,全栈开发逐渐成为一股不容忽视的趋势。无论你是经验丰富的开发者,还是刚刚入门的新手,掌握全栈技术都能让你在竞争中脱颖而出。而在这个过程…...
MySQL 实战 45 讲(01-05)
本文为笔者学习林晓斌老师《MySQL 实战 45 讲》课程的学习笔记,并进行了一定的知识扩充。 sql 查询语句的执行流程 大体来说,MySQL 可以分为 Server 层和存储引擎层两部分。 Server 层包括连接器、查询缓存、分析器、优化器和执行器。 连接器负责接收客…...
仓颉编程语言入门 -- Array数组详解
仓颉编程语言入门 – Array数组详解 一. 如何创建Array数组 我们可以使用 Array 类型来构造单一元素类型,有序序列的数据。 1.仓颉使用 Array 来表示 Array 类型。T 表示 Array 的元素类型,T 可以是任意类型 , 类似于泛型的概念 var arr:Array<St…...
C#初级——简单单例模式使用
单例模式 单例模式是一种常用的软件设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例,通过单例模式防止私有成员被多次引用,防止数据被随意纂改。本文使用的是线程不安全的懒汉式单例。 创建单例模式 首…...
2024.07.29 校招 实习 内推 面经
地/球🌍 : neituijunsir 交* 流*裙 ,内推/实习/校招汇总表格 1、校招 | 美/团// 快驴、小象、优/选/事/业/部2024年校/园/招聘(内推) 校招 | 美团快驴、小象、优选事业部2024年校园招聘(内推ÿ…...
速盾:爬虫攻击和cc攻击的区别是什么?
爬虫攻击和CC(Distributed Denial of Service)攻击是网络安全领域两种不同类型的攻击方式。尽管它们都涉及对目标网站或服务器的非法访问,但它们的目的、方法和影响各不相同。在接下来的文章中,我们将详细介绍这两种攻击方式的区别…...
Tomcat与Nginx的区别详解
目录 引言Tomcat概述 Tomcat的历史Tomcat的架构Tomcat的功能Nginx概述 Nginx的历史Nginx的架构Nginx的功能Tomcat与Nginx的区别 架构上的区别...
【大模型从入门到精通5】openAI API高级内容审核-1
这里写目录标题 高级内容审核利用 OpenAI 内容审核 API 的高级内容审核技术整合与实施使用自定义规则增强审核综合示例防止提示注入的策略使用分隔符隔离命令理解分隔符使用分隔符实现命令隔离 高级内容审核 利用 OpenAI 内容审核 API 的高级内容审核技术 OpenAI 内容审核 AP…...
JVM系列 | 对象的消亡3——垃圾收集器的对比与实现细节
垃圾收集器 文章目录 各收集器简单对比收集器启动参数各收集器详细说明JDK 1.3 之前JDK 1.3 | SerialJDK 1.4 | ParNewJDK 1.4 | Parallel ScavengeJDK 5 | CMS 收集器JDK 7 | G1 各收集器简单对比 收集器名称出现时间淘汰时间目标采用技术线程数STW分代备注无名JDK 1.3之前JD…...
Linux 文件类型,目录与路径,文件与目录管理
文件类型 后面的字符表示文件类型标志 普通文件:-(纯文本文件,二进制文件,数据格式文件) 如文本文件、图片、程序文件等。 目录文件:d(directory) 用来存放其他文件或子目录。 设备…...
以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
定时器任务——若依源码分析
分析util包下面的工具类schedule utils: ScheduleUtils 是若依中用于与 Quartz 框架交互的工具类,封装了定时任务的 创建、更新、暂停、删除等核心逻辑。 createScheduleJob createScheduleJob 用于将任务注册到 Quartz,先构建任务的 JobD…...
Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
GitHub 趋势日报 (2025年06月08日)
📊 由 TrendForge 系统生成 | 🌐 https://trendforge.devlive.org/ 🌐 本日报中的项目描述已自动翻译为中文 📈 今日获星趋势图 今日获星趋势图 884 cognee 566 dify 414 HumanSystemOptimization 414 omni-tools 321 note-gen …...
IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
