Bellman-Ford算法

1.算法概述

Bellman–Ford 算法由 Richard Bellman 和 Lester Ford 在 1958 年提出,用于解决带权图的单源最短路径问题

注意

虽然spfa算法在各方面均优于bellman-Ford算法,但是有边数限制类最短路问题只能使用bellman-Ford算法

  • 核心思想

  • 反复对所有边进行「松弛」操作,最多做 |V|–1 轮即可找到最短路径;再做一次松弛即可检测负权回路。

  • 特点

    • 可处理负权边

    • 能检测「负权回路」


2.算法适用条件

  • 任意带权有向图或无向图(将无向边视为两条相反方向的有向边)

  • 权重可为负,但若存在负权回路则无最短解,只能检测并报告


3.算法步骤(邻接表/矩阵通用版)

// 假设 vertices = 顶点数 n,edges 为边列表 [(u,v,w), ...]  
// dist[]、pre[] 全局或传参  
// 初始化
for (int i = 1; i <= n; i++) {
    dist[i] = INF;
    pre[i]  = -1;
}
dist[src] = 0;

// 松弛操作:最多做 n-1 轮
for (int i = 1; i < n; i++) {
    for (auto &e : edges) {
        int u = e.u, v = e.v, w = e.w;
        if (dist[u] != INF && dist[u] + w < dist[v]) {
            dist[v] = dist[u] + w;
            pre[v]  = u;
        }
    }
}

// 负权回路检测:再做一次松弛
for (auto &e : edges) {
    int u = e.u, v = e.v, w = e.w;
    if (dist[u] != INF && dist[u] + w < dist[v]) {
        // 存在负权回路
        return false;
    }
}

return true;  // 无负权回路,dist 中保存最短距离

4.复杂度

时间复杂度

实现方式时间复杂度适用场景
边列表松弛O(V·E)中小规模任意图
邻接矩阵扫描O(V³)V 很小且密集时可行

空间复杂度:O(V + E)


5.代码模板(C++)

#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;

struct Edge { int u, v, w; };

int n, m;                 // 顶点数 n,边数 m
vector<Edge> edges;
vector<int> dist, pre;

bool bellman_ford(int src) {
    dist.assign(n+1, INF);
    pre .assign(n+1, -1);
    dist[src] = 0;

    // 松弛 |V|-1 次
    for (int i = 1; i < n; i++) {
        bool updated = false;
        for (auto &e : edges) {
            if (dist[e.u] != INF && dist[e.u] + e.w < dist[e.v]) {
                dist[e.v] = dist[e.u] + e.w;
                pre[e.v]  = e.u;
                updated = true;
            }
        }
        if (!updated) break;  // 提前退出
    }

    // 负权回路检测
    for (auto &e : edges) {
        if (dist[e.u] != INF && dist[e.u] + e.w < dist[e.v])
            return false;
    }
    return true;
}

int main() {
    cin >> n >> m;
    edges.resize(m);
    for (int i = 0; i < m; i++)
        cin >> edges[i].u >> edges[i].v >> edges[i].w;

    if (!bellman_ford(1)) {
        cout << "存在负权回路\n";
    } else {
        for (int i = 1; i <= n; i++)
            cout << "1→" << i << " 最短距离 = "
                 << (dist[i]==INF ? -1 : dist[i]) << "\n";
    }
    return 0;
}

6.常见问题


Q1:为什么要做 n-1 轮松弛?

因为任何一条最短路径最多经过 n-1 条边。

Q2:如何提前终止?

若某一轮松弛中未发生任何松弛(updated = false),可提前退出主循环。

Q3:如何输出具体路径?

使用 pre[] 数组从终点逆推到源点,再逆序打印即可。

Q4:能否用于无向图?

可以,将每条无向边 (u–v,w) 拆为两条有向边 (u→v,w)、(v→u,w)。


7.例题


8.声明


以上旨在总结 Bellman–Ford 算法要点,如有错误或不全,欢迎指正!

总结:

  • Bellman–Ford 算法通过反复松弛实现单源最短路径计算,能处理负权边并检测负权回路。

  • 适用于顶点数较小、需考虑负权边或回路检测的场合。

  • 理解松弛操作与负回路检测是掌握该算法的关键。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值