【数据结构与算法 | 图篇】Bellman-Ford算法(单源最短路径算法)

1. 前言

前文的迪杰斯特拉算法不能求解有负边的图的最短路径的问题。而此文的Bellman-Ford可以处理含负权边的图算法,并且能检测出图中是否存在负环(权重和为负数的环).

2. 基本思想

1. 初始化:

  • 对于所有顶点 v ∈ V \ {s}(除了起点 s),设其到起点的距离为无穷大(表示不可达)。
  • 起点 s 到自身的距离设为 0。


2. 松弛操作:

  • 遍历图中的每条边 (u, v) ∈ E,执行松弛操作 `Relax(u, v, w)`。松弛操作尝试通过边 (u, v) 更新从起点 s 到顶点 v 的已知最短距离。
  • 如果存在一条从起点 s 到顶点 u 的更短路径,并且这条路径加上边 (u, v) 的权重小于目前记录的从起点 s 到顶点 v 的距离,则更新顶点 v 的距离值。
  • 这个过程需要重复进行 |V| - 1 次(V 是顶点集)。因为在没有负权环的情况下,任何从起点到某个顶点的最短路径最多包含 |V| - 1 条边。

3. 检查负权环:

  •  在进行了 |V| - 1 轮松弛操作之后,再进行一轮松弛操作。如果在这个过程中仍然能够进一步减少某个顶点的距离值,那么说明图中存在一个可以被利用来无限降低路径成本的负权环。

3. 顶点类代码

public class Vertex {
    // 顶点的名字,用来区分顶点
    String name;
    // 与该顶点有关的边的集合
    List<Edge> edges;
    // 判断是否已经被遍历
    boolean visited = false;
    // 初始距离为无穷大
    int dist = INF;
    // INF表示无穷大
    final static int INF = Integer.MAX_VALUE;
    // 该顶点在最短路径中的前一个顶点
    Vertex prev = null;
    public Vertex(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

顶点图:

4. Bellman-Ford算法代码

public class BellmanFord {
    public static void main(String[] args) {
        Vertex v1 = new Vertex("1");
        Vertex v2 = new Vertex("2");
        Vertex v3 = new Vertex("3");
        Vertex v4 = new Vertex("4");

        v1.edges = new ArrayList<>();
        v1.edges.add(new Edge(v2, 2));
        v1.edges.add(new Edge(v3, 1));

        v2.edges = new ArrayList<>();
        v2.edges.add(new Edge(v3, -2));

        v3.edges = new ArrayList<>();
        v3.edges.add(new Edge(v4, 1));

        v4.edges = new ArrayList<>();
        List<Vertex> graph = new ArrayList<>();
        graph.add(v1);
        graph.add(v2);
        graph.add(v3);
        graph.add(v4);
        // v1作为起始点
        bellmanford(graph, v1);
    }
    public static void bellmanford(List<Vertex> graph, Vertex source){
        // 将起始点的距离设置为0,其余点的距离都是无穷大
        source.dist = 0;
        int size = graph.size();
        // 进行 顶点数-1 次处理
        for(int k = 0; k < size - 1; k++) {
            // 遍历所有的边
            for(Vertex v : graph){
                for(Edge e : v.edges){
                    // 处理每条边
                    if(v.dist != Integer.MAX_VALUE &&
                       v.dist + e.weight < e.linked.dist){
                        e.linked.dist = v.dist + e.weight;
                        e.linked.prev = v;
                    }
                }
            }
        }
        for(Vertex v : graph){
            System.out.println("v" + v.name + "  " + v.dist);
        }
    }
}

打印的结果:

v1  0
v2  2
v3  0
v4  1
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值