Bellman-Ford 算法 C++ 实现:负权图最短路径与负权环检测 5 步详解

发布时间:2026/9/29 2:40:21

Bellman-Ford 算法 C++ 实现:负权图最短路径与负权环检测 5 步详解
Bellman-Ford 算法 C 实现负权图最短路径与负权环检测 5 步详解1. 算法核心思想与适用场景Bellman-Ford 算法是图论中解决单源最短路径问题的经典算法由 Richard Bellman 和 Lester Ford 在 20 世纪 50 年代提出。与 Dijkstra 算法相比它的独特优势在于能够处理含有负权边的图同时具备检测负权环的能力。算法核心机制通过V-1 次松弛操作V 为顶点数逐步逼近最短路径每次迭代对所有边进行松弛更新源点到各顶点的最短距离最终通过额外迭代检测图中是否存在负权环典型应用场景网络路由协议如 RIP金融系统中的套利检测交通规划中的成本计算游戏开发中的路径寻找// 基础算法框架 for (int i 1; i V-1; i) { for (每条边(u,v)) { if (dist[u] w dist[v]) { dist[v] dist[u] w; } } }2. C 实现基础架构我们首先构建图的邻接表表示这是实现算法的数据结构基础#include vector #include climits struct Edge { int src, dest, weight; }; class Graph { private: int V; // 顶点数 std::vectorEdge edges; public: Graph(int vertices) : V(vertices) {} void addEdge(int u, int v, int w) { edges.push_back({u, v, w}); } bool bellmanFord(int src, std::vectorint dist) { // 初始化距离数组 dist.assign(V, INT_MAX); dist[src] 0; // 主松弛循环 for (int i 1; i V-1; i) { bool updated false; for (const auto edge : edges) { if (dist[edge.src] ! INT_MAX dist[edge.src] edge.weight dist[edge.dest]) { dist[edge.dest] dist[edge.src] edge.weight; updated true; } } if (!updated) break; // 提前终止优化 } // 负权环检测 for (const auto edge : edges) { if (dist[edge.src] ! INT_MAX dist[edge.src] edge.weight dist[edge.dest]) { return false; // 存在负权环 } } return true; } };3. 关键实现细节与优化3.1 松弛操作与提前终止Bellman-Ford 的核心在于松弛操作Relaxation其数学表示为dist[v] min(dist[v], dist[u] w(u,v))优化技巧提前终止当某次迭代没有发生任何距离更新时算法已收敛随机化处理顺序某些情况下可以加速收敛// 优化后的松弛循环 for (int i 1; i V-1; i) { bool updated false; for (const auto edge : edges) { if (relax(edge.src, edge.dest, edge.weight)) { updated true; } } if (!updated) break; // 提前终止 } bool relax(int u, int v, int w) { if (dist[u] ! INT_MAX dist[u] w dist[v]) { dist[v] dist[u] w; return true; } return false; }3.2 负权环检测机制负权环检测是算法的重要特性实现时需注意必须在完成 V-1 次迭代后执行只需检查是否能继续松弛无需实际遍历环检测逻辑对比表情况检测结果处理方式正常收敛无负权环返回正确距离仍可松弛存在负权环标记不可解4. 完整实现与测试用例下面是一个完整的实现包含路径重建功能#include iostream #include vector #include climits class BellmanFord { private: struct Edge { int src, dest, weight; }; int V; std::vectorEdge edges; public: BellmanFord(int vertices) : V(vertices) {} void addEdge(int u, int v, int w) { edges.push_back({u, v, w}); } bool solve(int src, std::vectorint dist, std::vectorint prev) { dist.assign(V, INT_MAX); prev.assign(V, -1); dist[src] 0; // 主算法循环 for (int i 1; i V-1; i) { bool updated false; for (const auto e : edges) { if (dist[e.src] ! INT_MAX dist[e.src] e.weight dist[e.dest]) { dist[e.dest] dist[e.src] e.weight; prev[e.dest] e.src; updated true; } } if (!updated) break; } // 负权环检测 for (const auto e : edges) { if (dist[e.src] ! INT_MAX dist[e.src] e.weight dist[e.dest]) { return false; } } return true; } void printPath(const std::vectorint prev, int v) { if (v 0) return; printPath(prev, prev[v]); std::cout v ; } }; // 测试用例 void testBellmanFord() { BellmanFord g(5); g.addEdge(0, 1, -1); g.addEdge(0, 2, 4); g.addEdge(1, 2, 3); g.addEdge(1, 3, 2); g.addEdge(1, 4, 2); g.addEdge(3, 1, 1); g.addEdge(3, 2, 5); g.addEdge(4, 3, -3); std::vectorint dist, prev; if (g.solve(0, dist, prev)) { for (int i 0; i 5; i) { std::cout Distance to i : dist[i] \tPath: ; g.printPath(prev, i); std::cout \n; } } else { std::cout Graph contains negative weight cycle!\n; } }5. 高级优化与工程实践5.1 SPFA 队列优化Shortest Path Faster Algorithm (SPFA) 是 Bellman-Ford 的队列优化版本bool spfa(int src, std::vectorint dist) { std::queueint q; std::vectorbool inQueue(V, false); std::vectorint count(V, 0); dist.assign(V, INT_MAX); dist[src] 0; q.push(src); inQueue[src] true; while (!q.empty()) { int u q.front(); q.pop(); inQueue[u] false; for (const auto e : edges) { if (e.src u dist[u] e.weight dist[e.dest]) { dist[e.dest] dist[u] e.weight; if (!inQueue[e.dest]) { q.push(e.dest); inQueue[e.dest] true; if (count[e.dest] V) { return false; // 负环检测 } } } } } return true; }5.2 实际工程注意事项数值溢出处理// 使用安全的加法防止溢出 if (dist[u] ! INT_MAX edge.weight 0 dist[u] INT_MAX - edge.weight) { // 处理溢出情况 }并行化可能性每轮迭代中的边处理可以并行执行需要原子操作保证距离更新的正确性内存访问优化// 按源点分组边可以提高缓存命中率 std::vectorstd::vectorEdge adj(V); for (const auto e : edges) { adj[e.src].push_back(e); }在实际项目中Bellman-Ford 算法常被用于网络路由协议和金融系统中的套利检测。我曾在一个高频交易系统中实现该算法来检测货币兑换环路的套利机会通过优化后的 SPFA 版本我们能够实时监控数十种货币对的汇率变化当发现负权环时立即触发交易策略。

相关新闻

NBM7100A与PIC18F4685在低功耗设备中的能量管理方案

NBM7100A与PIC18F4685在低功耗设备中的能量管理方案

2026/8/27 12:28:17

1. 项目背景与核心挑战在物联网和低功耗设备领域,如何最大化利用不可充电的初级电池(如锂锰电池)的能量一直是个棘手问题。这类电池在突发电流负载下表现尤为脆弱——电压骤降会导致设备意外重启,而电池内剩余的大量能量却无法被有…

GodotSteam游戏性能优化实战:10个技巧解决内存与网络瓶颈

GodotSteam游戏性能优化实战:10个技巧解决内存与网络瓶颈

2026/9/8 14:00:08

1. 项目概述:为什么GodotSteam项目需要性能优化?如果你正在用Godot引擎开发Steam平台的游戏,尤其是带有多人联机功能的项目,那么“性能”这个词,大概率已经让你头疼过不止一次了。我经历过不止一个项目,在开…

Node.js 核心模块实战:path 与 fs

Node.js 核心模块实战:path 与 fs

2026/9/24 9:29:28

从路径拼接到文件读写,掌握 Node.js 两大核心模块,并理解 JS 异步编程的完整进化链。📌 本文结构 path 模块(路径处理) → fs 模块(文件系统)→ 同步 vs 异步 → 异步进化史(回调 →…

CANN/GE ACL数据集缓冲区添加函数

CANN/GE ACL数据集缓冲区添加函数

2026/9/28 4:08:17

aclmdlAddDatasetBuffer 【免费下载链接】ge GE(Graph Engine)是面向昇腾的图编译器和执行器,提供了计算图优化、多流并行、内存复用和模型下沉等技术手段,加速模型执行效率,减少模型内存占用。 GE 提供对 PyTorch、Te…

用ffmpeg高效批量调整图片尺寸的实战指南

用ffmpeg高效批量调整图片尺寸的实战指南

2026/9/28 16:01:49

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

Transformers 音频特征提取工具库 audio_utils 全解析:从 Mel 刻度换算到对数 Mel 频谱

Transformers 音频特征提取工具库 audio_utils 全解析:从 Mel 刻度换算到对数 Mel 频谱

2026/9/28 2:15:29

Transformers 音频特征提取工具库 audio_utils 全解析:从 Mel 刻度换算到对数 Mel 频谱 【免费下载链接】transformers 🤗 Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and mu…

RustFS 多节点集群重启与滚动升级实战:Readiness、Quorum 与 Degraded 模式完全指南

RustFS 多节点集群重启与滚动升级实战:Readiness、Quorum 与 Degraded 模式完全指南

2026/9/28 3:14:54

RustFS 多节点集群重启与滚动升级实战:Readiness、Quorum 与 Degraded 模式完全指南 【免费下载链接】rustfs 🚀2.3x faster than MinIO for 4KB object payloads. RustFS is an open-source, S3-compatible high-performance object storage system sup…

Java Integer缓存揭秘:128陷阱原理、避坑与面试全解

Java Integer缓存揭秘:128陷阱原理、避坑与面试全解

2026/9/28 3:58:00

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

RustFS Scanner 数据用量发布权威性决策:配额准入如何获得可用的权威依据

RustFS Scanner 数据用量发布权威性决策:配额准入如何获得可用的权威依据

2026/9/28 3:47:14

RustFS Scanner 数据用量发布权威性决策:配额准入如何获得可用的权威依据 【免费下载链接】rustfs 🚀2.3x faster than MinIO for 4KB object payloads. RustFS is an open-source, S3-compatible high-performance object storage system supporting mi…

远程协作的工作台整理

远程协作的工作台整理

2026/9/28 16:01:48

远程协作的工作台整理远程协作的核心不是再加一个工具,而是让交接信息足够完整。异步任务要写明目标、输入位置、完成标准和需要决策的人。 工作台的最小配置 将日程、待办、代码和沟通入口收拢到少数固定位置;通知按紧急程度分层。工作台不需要模仿办公…

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

2026/9/28 5:05:21

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

2026/9/28 16:01:48

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…