惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - Franky
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
美团技术团队
The Cloudflare Blog
量子位
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园_首页
F
Fortinet All Blogs
J
Java Code Geeks
人人都是产品经理
人人都是产品经理
N
Netflix TechBlog - Medium
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
爱范儿
爱范儿
Apple Machine Learning Research
Apple Machine Learning Research
B
Blog RSS Feed
博客园 - 聂微东
Hugging Face - Blog
Hugging Face - Blog
WordPress大学
WordPress大学
小众软件
小众软件
Y
Y Combinator Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Vercel News
Vercel News
S
SegmentFault 最新的问题
有赞技术团队
有赞技术团队

博客园_首页

Linux实操--组管理、权限管理和定时任务 Java + EasyExcel 实现单个接口导出多个Excel Mem0 源码解析系列(二):提示词工程的深度剖析 Openclaw TaskFlow究竟是什么?和普通Skill技能有什么区别 博文阅读密码验证 - 博客园 嘉立创开源:应该是全网MicroPython教程最多的开发板 Hermes Agent 集成实践:从协议到生产 2026年AI编程工具横评:Cursor、Codex、Claude Code、Zed、Windsurf Java程序员必看的RAG入门教程 2026 AI效率神器:Superpowers + Claude Code 保姆级教程 本地大模型部署全攻略:从 0 到 1 玩转 Ollama 【从0到1构建一个ClaudeAgent】内存管理-上下文压缩 .NET 高级开发 | 设计、实现一个事件总线框架 电子小白入门之NE555 3. WorkBuddy:隐藏玩法,一键召唤专家,让 AI 以"专家身份"给你干活 和AI一起搞事情#3:Claude Teammate 游戏开发翻车实录 【OpenClaw】通过 Nanobot 源码学习架构---(7)Memory C# .NET 周刊|2026年3月3期 我在 Debian 11 上把 K8s 单机搭起来了,过程没你想的那么顺(/opt 目录版) 深度学习进阶(七)Data-efficient Image Transformer CLI+Skill搭建浏览器AI自动化框架,告别一切重复枯燥任务 告别Token账单无底洞:OpenClaw本地部署,重塑企业数据主权的唯一解 FastAPI+Vue:文件分片上传+秒传+断点续传,这坑我帮你踩平了! SBTI 爆火后,我做了个程序员版的 CBTI。。已开源 + 附开发过程 多模态检索开始进入工程期:用 Sentence Transformers 搭建可落地的 Multimodal RAG 100多行代码实现一个最简单的Agent(用ReAct) Claude Code 通关手册(八):推荐 5 个 Hooks,代码质量提升 3 倍 老板:“有人截图了!”。安全部门:“收到,马上查暗水印!” - why技术 技术之外,皆是人间 C#/.NET/.NET Core技术前沿周刊 | 第 69 期(2026年4.01-4.12)
2、BellMan-Ford算法
Carey_ccl · 2026-05-19 · via 博客园_首页

一、BellMan-Ford算法简介

  与Dijkstra算法一样,BellMan-Ford算法也是用于求有向图和无向图的单源最短路径的算法。但是,BellMan-Ford算法与Dijkstra算法的不同处,有以下2点:
①、BellMan-Ford算法可以用于边的权值为负数的有向图中,但该图中不能存在负值圈,也就是说,BellMan-Ford算法在计算无向图时,该无向图不能存在负值边(无向图的负值边就会存在负值圈),Dijkstra算法所计算的有向图和无向图中,边的权值都不能为负数;
②、BellMan-Ford算法是通过对边进行|V|-1次松弛操作来实现的,|V|是图中结点的数量,而Dijkstra算法是通过贪心法,选取未处理的结点中权值最小的结点,然后对选取的结点的邻接边进行松弛操作。

1.1、BellMan-Ford算法的松弛函数

  对于边集合E中的任意边,以w(u,v)表示结点u出发到结点v的边(Edge)的权值,以d[v]表示当前从起点s到结点v的路径权值,若存在边w(u,v),使得:

\[d[v]>d[u]+w(u,v) \]

则更新d[v]的值:

\[d[v]=d[u]+w(u,v) \]

所以松弛函数的作用,就是判断是否经过某个顶点,或者说经过某条边,可以缩短起点到终点的路径权值。

1.2、BellMan-Ford算法中松弛函数的执行次数

  现有所有结点a~d,如下图所示:
image

  用d[d]表示起点a到结点d的距离,用δ(a,d)表示起点a到结点d的最短路径权值,用p=<a,...,d> 表示结点a到结点d的路径,初始情况d[a]=0,d[v]=∞,v∈V-{a}。以对边集合 E 中每条边执行一次松弛函数作为一次迭代。那么,最好情况和最坏情况的分析,分别如下:
①、最好情况下,如果遍历松弛边的顺序为:w(a,b),w(b,c),w(c,d),其他两条边w(a,c),w(a,d)顺序无影响;

  • 第一次迭代
对边w(a,b)执行松弛函数,则d[b] = d[a]+w(a,b) = 1;
对边w(b,c)执行松弛函数,则d[c] = d[b]+w(b,c) = 3;
对边w(c,d)执行松弛函数,则d[d] = d[c]+w(c,d) = 8;

因为图结构比较简单,所以可以直接由观察得知,经过第一次迭代,即可得出从起点a到所有结点b、c、d的最短路径权值。
②、最坏情况下,如果遍历松弛边的顺序为:w(c,d),w(b,c)或w(b,c),w(c,d),其他三条边w(a,b),w(a,c),w(a,d)顺序无影响;

  • 第一次迭代
对边w(c,d)执行松弛函数,则d[d] = ∞;
对边w(b,c)执行松弛函数,则d[c] = ∞;
对边w(a,b)执行松弛函数,则d[b] = d[a] + w(a,b) = 1;
对边w(a,c)执行松弛函数,则d[c] = d[a] + w(a,c) = 6;
对边w(a,d)执行松弛函数,则d[d] = d[a] + w(a,d) = 10; 

第一次迭代,有三条边起到了松弛的效果,直观的可以看出 d[b] = δ(a,b),第一次迭代可以获得起点a到结点b的最短路径,路径为 P=<a,b>;

  • 第二次迭代
对边w(c,d)执行松弛函数,则d[d] = 10;
对边w(b,c)执行松弛函数,则d[c] = d[b]+w(b,c) = 3;
对边w(a,b)执行松弛函数,则d[b] = 1;
对边w(a,c)执行松弛函数,则d[c] = 6;
对边w(a,d)执行松弛函数,则d[d] = 10;

第二次迭代,有一条边起到了松弛的效果,直观的可以看出 d[c] = δ(a,c),第二次迭代可以获得起点a到结点b、结点c的最短路径,路径为 P=<a,b,c>;

  • 第三次迭代
对边w(c,d)执行松弛函数,则d[d] = d[c]+w(c,d) = 8;
对边w(b,c)执行松弛函数,则d[c] = 3;
对边w(a,b)执行松弛函数,则d[b] = 1;
对边w(a,c)执行松弛函数,则d[c] = 6;
对边w(a,d)执行松弛函数,则d[d] = 10;

第三次迭代,有一条边起到了松弛的效果,直观的可以看出 d[d] = δ(a,d),第三次迭代可以获得起点a到结点b、结点c、结点d的最短路径,路径为 P=<a,b,c,d>;

1.3、迭代次数分析(反证法)

  详细过程,请查看:https://www.jianshu.com/p/b876fe9b2338

二、代码实现

  有 n 个城市通过一些航班连接。给你一个数组 flights ,其中 flights[i] = [fromi, toi, pricei] ,表示该航班都从城市 fromi 开始,以价格 pricei 抵达 toi。现在给定所有的城市和航班,以及出发城市 src 和目的地 dst,你的任务是找到出一条最多经过 k 站中转的路线,使得从 src 到 dst 的 价格最便宜 ,并返回该价格。 如果不存在这样的路线,则输出 -1。
image
image
image

2.1、方法一

  Bellman Ford + 类(模拟边)的代码实现,如下所示:

class Solution {
    private class Edge {
        int src;
        int dest;
        int cost;

        public Edge(int src, int dest, int cost) {
            this.src = src;
            this.dest = dest;
            this.cost = cost;
        }
    }

    int N = 110;
    int INF = 0x3f3f3f3f;
    int limit, src, dest;
    ArrayList<Edge> list = null;
    int[] distance = new int[N];

    public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
        this.limit = k + 1;
        this.src = src;
        this.dest = dst;
        list = new ArrayList<Edge>();
        for (int[] flight : flights) {
            Edge edge = new Edge(flight[0], flight[1], flight[2]);
            list.add(edge);
        }
        int ans = dp();
        return ans > INF/2 ? -1 : ans;
    }

    public int dp() {
        Arrays.fill(distance, INF);
        distance[src] = 0;
        for (int i = 0; i < limit; i++) {
            int[] clone = distance.clone();
            for (Edge edge : list) {
                int src = edge.src, dest = edge.dest, cost = edge.cost;
                distance[dest] = Math.min(distance[dest], clone[src] + cost);
            }
        }
        return distance[dest];
    }
}
时间复杂度 共进行 k + 1次迭代,每次迭代备份数组复杂度为 O(n),n为所有结点的数量,然后遍历所有的边进行松弛操作,复杂度为 O(m),m为所有边的数量。整体复杂度为 O(k * (n + m))
空间复杂度 O(n + m),n为所有结点的数量,m为所有边的数量
2.2、方法二

  直接利用数组 flights 的Bellman Ford算法,如下所示:

class Solution {
    public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
        int INF = 0x3f3f3f3f;
        int limit = k + 1;
        int[] distance = new int[n];
        Arrays.fill(distance, INF);
        distance[src] = 0;
        for (int i = 0; i < limit; i++) {
            int[] clone = distance.clone();
            for (int[] flight : flights) {
                int s = flight[0], d = flight[1], cost = flight[2];
                distance[d] = Math.min(distance[d], clone[s] + cost);
            }
        }
        return distance[dst] > INF / 2 ? -1 : distance[dst];
    }
}
时间复杂度 共进行 k + 1次迭代,每次迭代备份数组复杂度为 O(n),n为所有结点的数量,然后遍历所有的边进行松弛操作,复杂度为 O(m),m为所有边的数量。整体复杂度为 O(k * (n + m))
空间复杂度 O(n + m),n为所有结点的数量,m为所有边的数量