keyipatience:个人主页作者简介C/C后端开发学习者专栏传送门《c》《linux》《c高阶数据结构》《c数据结构与算法》⭐️patience is key in lifeBellman-Ford算法Dijkstra仅支持正权图单源最短路朴素版复杂度 O (N²)效率高不能处理负权边无法检测负环。Bellman-Ford优势支持负权边的单源最短路还可以检测源点可达的负权回路。标准邻接表实现时间复杂度 O (N*E)如果用邻接矩阵实现就是咱们写的代码复杂度变成 O (N³)属于暴力松弛效率更差。缺点时间开销通常高于 Dijkstra速度慢。一句话概括Dijkstra 快但怕负权Bellman-Ford 能处理负权、判负环但代价是时间复杂度更高邻接矩阵写法会进一步恶化复杂度到 O (N³)。代码实现CLRS《算法导论》版本直接修改 dist 数组本轮更新的点本轮后面的边可以立刻被用到。 有可能提前就把长路径算出来不需要等到第 n‑1 轮。bool BellmanFord(const V src, vectorW dist, vectorint ppath) { int n _vertexs.size(); int srci GetVertexIndex(src); dist.resize(n, MAX_W);//dist[]的含义目前我们已经探索过的路径里起点 s 到这个点的最短距离 ppath.resize(n, -1); // 初始化ppath数组全部为-1 dist[srci] W();//源点到自己的距离为0; cout 依次选 i-j endl; for (int k 0; k n-1; k) { bool exchange false; for (int i 0; i n; i) { for (int j 0; j n; j) { //srci-uw(u-v)srci-v更新 if (_matrix[i][j] ! MAX_W dist[i] _matrix[i][j] dist[j]) { cout _vertexs[i] - _vertexs[j] -_matrix[i][j] endl;; dist[j] dist[i] _matrix[i][j]; ppath[j] i; exchange true; } } } return true; if (exchange false)break; } //检查有没有环路 for (int i 0; i n; i) { for (int j 0; j n; j) { if (_matrix[i][j] ! MAX_W dist[i] _matrix[i][j] dist[j]) { return false; } } } return true; }测试用例void TestGraphBellmanFord() { const char* str syztx; Graphchar, int, INT_MAX, true g(str, strlen(str)); g.AddEdge(s, t, 6); g.AddEdge(s, y, 7); g.AddEdge(y, z, 9); g.AddEdge(y, x, -3); g.AddEdge(z, s, 2); g.AddEdge(z, x, 7); g.AddEdge(t, x, 5); g.AddEdge(t, y, 8); g.AddEdge(t, z, -4); g.AddEdge(x, t, -2); vectorint dist; vectorint parentPath; if (g.BellmanFord(s, dist, parentPath)) { cout endl; g.PrinrtShotPath(s, dist, parentPath); } else { cout 存在负权回路 endl; } }结果下面我们来说一说这个算法比较难理解的点为什么外面要套一层for(int k0;kn-1;k)即为什么循环 n‑1 次首先先记住每一次循环指的是最外面k的一次循环进去都会重新把所有的边都尝试松弛即一次大循环---尝试松弛所有边1.先从拿边顺序的角度好理解我们先来看一个例子一个链图1→2→3n3n‑12情况 A边顺序[1→2 , 2→3]第一轮外层循环先松弛1→2dist[2]1紧接着松弛2→3直接用刚刚改好的 dist [2]dist [3]2仅仅 1 轮外层循环全部算完。updatedtrue不会 break。进入第二轮所有边都松弛不动updatedfalsebreak 跳出。实际有效工作只做了 1 轮。情况 B边顺序[2→3 , 1→2]第一轮外层循环先松弛2→3dist [2] 是无穷什么也做不了再松弛1→2dist[2]1第一轮结束dist [3] 依旧无穷。注意已经遍历过的边不会回头重新跑2→3已经处理完毕本轮不会再回来处理它。只能等下一轮大循环。第二轮外层循环 再次全部遍历边处理2→3dist [3] 才更新。这里实打实需要 2 轮n‑1 轮。所以如果暴力依次遍历所有边做松弛操作最坏情况下每一轮循环只能松弛成功一条边。而不含负权环的情况下最短路径最多含有 (n-1) 条边那么最坏情况就要循环 (n-1) 次每轮只能松弛 1 条边即n-1次后就能确保每条边都松弛了即找到最短距离。那如果还能再松弛呢为什么又说不含负权环的情况下最短路径最多含有 (n-1) 条边还能在松弛什么意思意思说我还能找到更短的路径可是按理说我n-1次循环下来n-1条边都已经松弛过了已经是最短的路径了呀。所以只能说明总的边数不是n-1而是存在环那到底是正权环还是负权环答案肯定是负权环因为只有负数才能使路径减小呀才能继续松弛下去。所以也能解释如果没有负权环的情况下最短路径最多就只有n-1条边2.我们也可以从具体的过程来理解再次看到打印结果我们分析一下依次选出的边看看哪里有问题所以在dist[t]2松弛更新后应该再用新更新的dist[t]2再对dist[z]松弛更新呀。即就只能等下一次循环进来后又一次对每一条边进行松弛的时候完成了这一次用的就是这个新的dist了。同样的这只是这1条边发生了这样的情况要是不带负权环一共n-1条边呢那就一共就需要n-1次循环补充双数组DP 原版才是符合 Bellman-Ford 数学定义斯坦福 / MIT 算法讲义的标准定义版本性质同一轮内永远只用本轮开始前的旧距离本轮新更新的值本轮不能复用struct Edge { int u, v, w; // u起点v终点w边权 } edges[M]; int old_dist[N]; // 上一轮迭代结束后的距离数组本轮全程只读不能修改 int new_dist[N]; // 保存本轮松弛计算出来的新距离 int n, m, s; // n顶点数量m边数s起点 // Bellman-Ford算法返回true代表图存在负权回路false无负环 bool bellman_ford() { // 初始化距离数组0x3f代表无穷大起点距离设为0 memset(old_dist, 0x3f, sizeof old_dist); old_dist[s] 0; // 最多循环 n-1 轮最短路径最多包含 n-1 条边 for(int i 1; i n - 1; i) { //① 本轮开始把旧距离拷贝到new_distnew_dist初始等于上一轮结果 memcpy(new_dist, old_dist, sizeof new_dist); // 遍历全部m条边做松弛操作 for(int j 0; j m; j) { int u edges[j].u; int v edges[j].v; int w edges[j].w; // 如果u可达并且经过u到v的路径更短 if(old_dist[u] ! 0x3f3f3f3f new_dist[v] old_dist[u] w) { new_dist[v] old_dist[u] w; // 更新v的最短距离 } } bool updated false; //标记本轮有没有任何点的距离被更新 for(int k 1; k n; k) if(new_dist[k] ! old_dist[k]) { updated true; break; } //② 本轮全部边松弛完毕把本轮结果保存到old_dist作为下一轮的旧距离 memcpy(old_dist, new_dist, sizeof old_dist); if(!updated) break; //本轮没有任何更新提前退出后面不会再优化了 } // 负环检测 // 再遍历一遍所有边如果还能松弛说明存在负权回路 for(int j 0; j m; j) { int u edges[j].u; int v edges[j].v; int w edges[j].w; if(old_dist[u] ! 0x3f3f3f3f old_dist[v] old_dist[u] w) { return true; // 还能松弛存在负权环 } } return false; //无负环 }2次memcpy只读取 old_dist旧数组只写入 new_dist新数组第一次memcpynew_dist ← old_dist含义 本轮一开始先把上一轮的结果全部复制一份给 new_dist。就像是换个名字在new_dist的基础上修改不去改old第二次memcpyold_dist ← new_dist含义本轮所有边处理完毕本轮的全部计算结果都存在 new_dist 里现在把本轮的最终结果整体拷贝到 old_dist作为下一轮迭代的 “旧基准数组”。和 原版DP 公式对应d(k)[v] min( d(k-1)[v], d(k-1)[u]w )• d(k−1) → old_dist• d(k) → new_dist第一次 memcpyd (k)[v] 初始化为 d (k−1)[v]第二次 memcpy本轮计算完成把 d (k) 交给 old_dist作为下一轮的 d (k−1)重点是通过这个双数组的方式我们能很好理解为什么要n-1次循环。3 个顶点 n3s → t → z边权都是 1顶点stz边s→t (1)t→z (1) n3所以最多循环n-12 轮初始old_dist [0, ∞, ∞]s 起点距离 0t、z 无穷大第 1 轮k1最多走 1 条边memcpyo-n)new_dist [0, ∞, ∞]遍历所有边s→told_dist[s]1 011 ∞ → new_dist[t]1t→zold_dist [t] 是∞无法更新本轮结束memcpy 把 new_dist 给 old_dist 现在 old_dist [0, 1, ∞] 本轮算出最多 1 条边能到达的点t第 2 轮k2最多走 2 条边memcpynew_dist [0, 1, ∞]遍历所有边s→told_dist [s]11不比 new_dist [t] 更小不变t→zold_dist[t]1 112 ∞ → new_dist[z]2本轮结束memcpyold_dist [0,1,2] 本轮算出最多 2 条边到达 z现在 2 轮跑完n-12所有简单路径全部算完。 简单路径不能重复经过顶点3 个点最多 2 条边不可能存在 3 条边的无重复点路径。如果再跑第 3 轮k3再次遍历边已经找不到可以松弛更新的点了距离不会再变小。如果第 3 轮还能更新说明图里存在负环。总结每一轮外层循环只能基于上一轮old_dist多拓展1 条边。本轮内部产生的新距离本轮不能拿来用下一轮才生效。第 1 轮只能算出最多1 条边的路径源点直接相连第 2 轮可以算出最多2 条边的路径…第 k 轮可以算出最多k 条边的路径想要算出拥有 n‑1 条边的那条最长无环路径就必须执行到第 n‑1 轮。每次循环只能往外扩展1条边n-1条边就要n-1次循环2者对比效率单数组更高双数组绝大多数情况必须跑满 n-1 次如果有不可达的情况就直接break了虽然也不用一定要n-1次但情况很少eg链图 1→2→3n3单数组边顺序1→22→31 轮全部更新完第二轮直接 break总共 2 轮循环但第二轮只是扫一遍很快退出双数组第 1 轮只能更新点 2第 2 轮才能更新点 3。两轮跑完才全部更新完成无法压缩到 1 轮
企业数字化 ERP 产品动态
相关推荐
【软考信息安全】第五章 通信线路、设备与存储介质安全 本文基于软考信息安全工程师课程笔记整理,系统梳理网络通信线路安全防护、设备实体安全威胁与防护、硬件攻击检测技术、存储介质安全管理与容灾备份技术,涵盖物理安全中"线路-设备-数据"三层防护的核心考点。一、网络通信线路安全分析与防护
1… · 2026/9/26 11:13:09
【软考信息安全】第六章 认证技术基础与原理 本文基于软考信息安全工程师课程笔记整理,系统梳理认证技术的概念体系、认证机制三要素、单向/双向/第三方认证流程、PPP协议认证(PAP/CHAP)、Kerberos协议、PKI体系及其他新兴认证技术,涵盖身份认证领域的核心考点与协议流程。一… · 2026/9/26 11:13:09
Jev模型:不写文章、不聊天的“决策机器” 前言最近有一个叫 Jev 的模型在开发者圈子里刷屏了。它由前 OpenAI 研究员 Diogo Almeida 创办的 TypeSafe AI 发布,上线 24 小时内,Vercel AI Gateway 上近 13% 的付费团队就开始用它,速度是之前所有新模型上线首日的两倍以上。有意思的是&a… · 2026/9/26 11:13:09
交易所2.0开发者生态:从API设计到行情与订单实战指南 这两年,“加密货币交易所2.0”从一个营销口号慢慢变成了真刀真枪的行业现实。以前各家拼的是首页Banner、手续费折扣、拉新返佣,谁广告砸得多,谁就能把交易量冲上去;现在风向变了,能稳定提供低延迟行情、灵活下单接口、… · 2026/9/26 11:47:26
OpenCart 后台 CMS 文章管理完全指南:从发布、多语言与多商店配置到 SEO 与评论优化 电商后端 【免费下载链接】opencart A free shopping cart system. OpenCart is an open source PHP-based online e-commerce solution. 项目地址: https://gitcode.com/gh_mirrors/op/opencart 点击查看 免费下载 导读
本文以 OpenCart 官方文档 docs/admin-int… · 2026/9/26 11:47:26
VirtualBox安装Windows 11 EFI启动失败深度解析 1. 为什么在 VirtualBox 里装 Windows 11 总是卡在 EFI 启动失败、硬盘找不到? 我第一次在 VirtualBox 里装 Windows 11 是去年 10 月,用的是官方 ISO 镜像,配置了 4G 内存、2 核 CPU、64GB 动态分配虚拟硬盘——结果卡在黑屏加光标闪烁&… · 2026/9/26 11:47:26
前后端分离微信小程序全栈开发:Django+Vue+MySQL实战指南 简介:一套面向计算机专业毕业设计场景的家庭大厨微信小程序完整工程,后端基于Python Django,前端使用Vue,小程序端采用微信开发者工具,数据库选用MySQL,整体前后端分离,便于拆分学习与二次开发。… · 2026/9/26 11:47:26
领英成为AI问答新来源:身份信用与一线经验的结合 最近跟几个做AI的朋友聊天,发现一个反直觉的共识:大家现在遇到人工智能相关的问题,第一反应不是去传统的技术问答社区,也不完全是问AI助手,而是先去领英上搜一圈。用他们的话说,领英正逐渐变成人工智能问答… · 2026/9/26 11:47:20
K8s Resource深度解析:从资源配额到RBAC权限与403排查 1. 先搞明白Resource到底在说什么刚接触Kubernetes的同学,十有八九会被Resource这个词搞懵。它不是单一概念,而是一整套贯穿集群运行机制的设计。Kubelet、Scheduler、API Server、RBAC权限模型、甚至前端页面的静态文件,全都和Resource相关。… · 2026/9/26 11:47:20
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46