Bellman–Ford Algorithm

算法参考地址:Bellman–Ford Algorithm | DP-23 - GeeksforGeeks

算法的简介

在图中给定一个图形和一个源顶点 src,查找从 src 到给定图中所有顶点的最短路径。该图可能包含负权重边。 我们已经讨论了[Dijkstra针对这个问题的算法]。Dijkstra的算法是一种贪婪算法,时间复杂度为O(V^2),(使用斐波那契堆)的时间复杂度为O((V+E)LogV)。Dijkstra不适用于负权重的图形,Bellman-Ford适用于此类图形。Bellman-Ford也比Dijkstra更简单,并且非常适合分布式系统。但贝尔曼-福特的时间复杂度是O(VE)。

1)负权重在图形的各种应用中都可以找到,比如 (1):计算化学反应经过N环节得到另一种物质,其中(1~N)中的某个环节可能是吸收能量也可能是释放能量。(2):计算电子在原子核外轨道的跃迁吸收或释放的能量等等。 2)Bellman-Ford在分布式系统中工作得更好(比Dijkstra更好)。与Dijkstra不同,我们需要找到所有顶点的最小值,在Bellman-Ford中,边是逐个考虑的。 3)贝尔曼-福特不适用于具有负边的无向图,因为它将被声明为负循环。

算法的过程

以下是详细步骤。 输入:图形和源顶点 src 输出:src 到所有顶点的最短距离。如果存在负重周期,则不计算最短距离,报告负重周期。 1) 此步骤将源到所有顶点的距离初始化为无穷大,到源本身的距离初始化为 0。创建大小为 | 的数组 dist[]五|所有值均为无穷大,但 dist[src] 除外,其中 src 是源顶点。 2) 此步骤计算最短距离。执行以下|V|-1倍,其中|五|是给定图形中的顶点数。 .....a) 对每个边缘 u-v 执行跟踪操作。如果 dist[v] > dist[u] + 边缘 uv 的权重,则更新 dist[v] ......................dist[v] = dist[u] + 边缘 uv3 的权重) 此步骤报告图中是否存在负权重周期。对每个边缘u-v 做以下操作......如果 dist[v] > dist[u] + 边缘 uv 的权重,则"图形包含负权重循环" 步骤 3 的想法是,如果图形不包含负权重循环,则步骤 2 保证最短距离。如果我们再次迭代所有边,并为任何顶点获得更短的路径,那么就会出现负权重循环 这是如何工作的?与其他动态规划问题一样,该算法以自下而上的方式计算最短路径。它首先计算路径中最多有一条边的最短距离。然后,它计算最多包含 2 条边的最短路径,依此类推。在外循环的第 i 次迭代之后,将计算最多 i 条边的最短路径。最大|五|– 任何简单路径中的 1 条边,这就是外循环运行 |v|– 1倍。这个想法是,假设没有负权重周期,如果我们计算了最多i条边的最短路径,那么对所有边的迭代保证给出最多(i + 1)条边的最短路径 示例 让我们通过下面的示例图来理解算法。 设给定的源顶点为 0。将所有距离初始化为无穷大,但到源本身的距离除外。图中的顶点总数为 5,因此必须处理所有边 4 次。

让所有边按以下顺序进行处理:(B, E)、(D、 B)、(B、 D)、(A、 B)、(A、C)、(D、C)、(B、C)、(E、D)。当第一次处理所有边时,我们得到以下距离。第一行显示初始距离。第二行显示处理边 (B、 E)、(D、 B)、(B、 D) 和 (A、 B) 时的距离。第三行显示处理 (A, C) 时的距离。第四行显示处理 (D、 C)、(B、 C) 和 (E、 D) 的时间。

第一次迭代保证给出所有最短路径,这些路径的长度最多为 1 条边。当第二次处理所有边时,我们得到以下距离(最后一行显示最终值)。

第二次迭代保证给出所有最短路径,这些路径的长度最多为 2 条边。该算法再处理所有边缘 2 次。距离在第二次迭代后最小化,因此第三次和第四次迭代不会更新距离。

算法的实现

golang

type Edge struct {
startVertex int
endVertex   int
weight      int
}
// F 代表两点之间不可达
const F = 10000
func bellmanFord(graph [][]int, source int) []int {
  edges := make([]Edge, 0)
  n := len(graph)
  //邻接矩阵转换为边表示
  for i := 0; i < n; i++ {
     for j := 0; j < n; j++ {
        if graph[i][j] != F {
           edges = append(edges, Edge{i, j, graph[i][j]})
        }
    }
  }

  dist := make([]int, n)
  for i := 0; i < n; i++ {
     dist[i] = F
  }
  dist[source] = 0
  for i := 1; i < n; i++ {
     for _, edge := range edges {
        start := edge.startVertex
        end := edge.endVertex
        if dist[start] != F && dist[end] > dist[start]+edge.weight {
           dist[end] = dist[start] + edge.weight
        }
    }
  }

  for _, edge := range edges {
     start := edge.startVertex
     end := edge.endVertex
     if dist[start] != F && dist[end] > dist[start]+edge.weight {
        fmt.Println("Graph contains negative weight cycle")
        return []int{}
    }
  }
  return dist
}

Java

package graph.bellman_ford;

import lombok.Data;

public class Graph {
   private final int vertexCount;
   private final int edgeCount;
   private final Edge[] edge;

   public Graph(int vertexCount, int edgeCount, Edge[] edge) {
       this.vertexCount = vertexCount;
       this.edgeCount = edgeCount;
       this.edge = edge;
  }

   @Data
   public static class Edge {
       Vertex source;
       Vertex destination;
       int weight;
  }

   @Data
   public static class Vertex {
       int sequence;
       String code;
       String name;
  }


   public void bellmanFord(Graph graph, int src) {
       int[] distance = new int[vertexCount];
       
       for (int i = 0; i < vertexCount; ++i) {
           distance[i] = Integer.MAX_VALUE;
      }
       distance[src] = 0;

 
       for (int i = 1; i < vertexCount; ++i) {
           for (int j = 0; j < edgeCount; ++j) {
               int u = graph.edge[j].source.sequence;
               int v = graph.edge[j].destination.sequence;
               int weight = graph.edge[j].weight;
               if (distance[u] != Integer.MAX_VALUE && distance[u] + weight < distance[v]) {
                   distance[v] = distance[u] + weight;
              }
          }
      }

   
       for (int j = 0; j < edgeCount; ++j) {
           int u = graph.edge[j].source.sequence;
           int v = graph.edge[j].destination.sequence;
           int weight = graph.edge[j].weight;
           if (distance[u] != Integer.MAX_VALUE && distance[u] + weight < distance[v]) {
               return;
          }
      }
       printArr(distance, vertexCount);
  }

   public void printArr(int[] distance, int vertexCount) {
       for (int i = 0; i < vertexCount; ++i) {
           System.out.println(i + "\t\t" + distance[i]);
      }
  }
}

package graph.bellman_ford;

import java.util.ArrayList;
import java.util.List;

public class ShortestPathOfBellmanFord {

   public static void main(String[] args) {


       List<Graph.Vertex> vertexList = new ArrayList<>();
       for (int i = 0; i < 5; i++) {
           Graph.Vertex vertex = new Graph.Vertex();
           vertex.code = "code" + i;
           vertex.name = "name" + i;
           vertex.sequence = i;
           vertexList.add(vertex);
      }
       Graph.Edge[] edges = new Graph.Edge[8];
       for (int i = 0; i < edges.length; i++) {
           edges[i] = new Graph.Edge();
      }

       // edge 0 --> 1
       edges[0].source = vertexList.get(0);
       edges[0].destination = vertexList.get(1);
       edges[0].weight = -1;


       // edge 0 --> 2
       edges[1].source = vertexList.get(0);
       edges[1].destination = vertexList.get(2);
       edges[1].weight = 4;

       // edge 1 --> 2
       edges[2].source = vertexList.get(1);
       edges[2].destination = vertexList.get(2);
       edges[2].weight = 3;

       // edge 1 --> 3
       edges[3].source = vertexList.get(1);
       edges[3].destination = vertexList.get(3);
       edges[3].weight = 2;

       // edge 1 --> 4
       edges[4].source = vertexList.get(1);
       edges[4].destination = vertexList.get(4);
       edges[4].weight = 2;

       // edge 3 --> 2
       edges[5].source = vertexList.get(3);
       edges[5].destination = vertexList.get(2);
       edges[5].weight = 5;

       // edge 3 --> 1
       edges[6].source = vertexList.get(3);
       edges[6].destination = vertexList.get(1);
       edges[6].weight = 1;

       // edge 4--> 3
       edges[7].source = vertexList.get(4);
       edges[7].destination = vertexList.get(3);
       edges[7].weight = -3;


       Graph graph = new Graph(5, 8, edges);
       graph.bellmanFord(graph, 0);
  }
}

图最短路径之BellmanFord的更多相关文章

  1. 【算法导论】单源最短路径之Bellman-Ford算法

    单源最短路径指的是从一个顶点到其它顶点的具有最小权值的路径.我们之前提到的广度优先搜索算法就是一种无权图上执行的最短路径算法,即在所有的边都具有单位权值的图的一种算法.单源最短路径算法可以解决图中任意 ...

  2. python数据结构与算法——图的最短路径(Bellman-Ford算法)解决负权边

    # Bellman-Ford核心算法 # 对于一个包含n个顶点,m条边的图, 计算源点到任意点的最短距离 # 循环n-1轮,每轮对m条边进行一次松弛操作 # 定理: # 在一个含有n个顶点的图中,任意 ...

  3. 单源最短路径的Bellman-Ford 算法

    1.算法标签 BFS 2.算法概念 Bellman-Ford算法有这么一个先验知识在里面,那就是最短路径至多在N步之内,其中N为节点数,否则说明图中有负权值的回路,这样的图是找不到最短路径的.因此Be ...

  4. 最短路径之Bellman-Ford——解决负权边

    Bellman-Ford算法非常简单,核心代码四行,可以完美的解决带有负权边的图. for(k=1;k<=n-1;k++) //外循环循环n-1次,n为顶点个数 for(i=1;i<=m; ...

  5. 单源最短路径算法——Bellman-ford算法和Dijkstra算法

     BellMan-ford算法描述 1.初始化:将除源点外的所有顶点的最短距离估计值 dist[v] ← +∞, dist[s] ←0; 2.迭代求解:反复对边集E中的每条边进行松弛操作,使得顶点集V ...

  6. 【算法】单元最短路径之Bellman-Ford算法和SPFA算法

    SPFA是经过对列优化的bellman-Ford算法,因此,在学习SPFA算法之前,先学习下bellman-Ford算法. bellman-Ford算法是一种通过松弛操作计算最短路的算法. 适用条件 ...

  7. 图-最短路径-Dijktra(迪杰斯特拉)算法

    1. 迪杰斯特拉算法是由荷兰计算机科学家狄克斯特拉算法于1959 年提出的,因此又叫狄克斯特拉算法.是从一个顶点到其余各顶点的最短路径算法,解决的是有向图中最短路径问题.迪杰斯特拉算法主要特点是以起始 ...

  8. 数据结构——图——最短路径D&F算法

    一.Dijkstra算法(贪心地求最短距离的算法) 在此算法中,我按照自己的理解去命名,理解起来会轻松一些. #define MAXSIZE 100 #define UNVISITED 0 #defi ...

  9. 图->最短路径->多源最短路径(弗洛伊德算法Floyd)

    文字描述 求每一对顶点间的最短路径,可以每次以一个顶点为源点,重复执行迪杰斯特拉算法n次.这样,便可求得每一对顶点之间的最短路径.总的执行时间为n^3.但是还有另外一种求每一对顶点间最短路径的方法,就 ...

  10. 图->最短路径->单源最短路径(迪杰斯特拉算法Dijkstra)

    文字描述 引言:如下图一个交通系统,从A城到B城,有些旅客可能关心途中中转次数最少的路线,有些旅客更关心的是节省交通费用,而对于司机,里程和速度则是更感兴趣的信息.上面这些问题,都可以转化为求图中,两 ...

随机推荐

  1. 009_原理图中电气互连,Net alias,分页符,总线

    009_原理图中电气互连,Net alias,分页符,总线 1.电气互连,就是画线. 2.端口名,适用同一页相连的端口. 3.分页符off page connector,适用于不同页的端口连接. 4. ...

  2. 入门Semantic Kernel:OneApi集成与HelloWorld

    引言 从这一章节开始正式进入我们的 Semantic Kernel 的学习之旅了. 什么是Semantic Kernel? Semantic Kernel是一个轻量级的开源框架,通过 Semantic ...

  3. python Requests 库的使用

    目录 1. 介绍 2. 安装 3. 基本请求 3.1 get请求 3.2 post请求 3.3 自定义请求头部 3.4 设置超时时间 3.5 代理访问 3.6 session自动保存cookies 3 ...

  4. ibus 输入法导致输入卡顿的解决方案

    系统: Zorin OS 16 Pro 基于 Ubuntu 20.04 LTS 关键词:Linux 间歇性卡顿.输入法导致卡顿.无法输入 本问题发生的情形是系统间歇性的无法接受键盘输入,无意间发现切换 ...

  5. python教程6.3-time模块datetime模块

     由于time是基于Unix Timestamp,所以其所能表述的日期范围被限定在 1970 – 2038 之间.因此2038年后就不能用time了,建议使用datetime. time模块 有下面几 ...

  6. C数据结构线性表:实现顺序表的增删改查&完整篇

    文章目录 ①前言 顺序表结构体的定义 ②初始化顺序表 ③插入新的元素 插入的时候需要特别注意的几点 ④删除元素 第一个删除元素功能实现 第二个删除元素功能实现 对代码下面中**i- -**的说明(第二 ...

  7. Dubbo SPI扩展机制源码详解(基于2.7.10)

    Dubbo SPI 一. 概述 本文主要分享 Dubbo 的拓展机制 SPI. 想要理解 Dubbo ,理解 Dubbo SPI 是非常必须的.在 Dubbo 中,提供了大量的拓展点,基于 Dubbo ...

  8. Servlet转发与重定向的资源路径问题解析

    一. 问题引入 转发和重定向可以说是Servlet中最重要的知识点也不为过,因为它决定着整个向Servlet体系中,执行流程的问题.      转发: request.getRequestDispat ...

  9. Pandas学习之路【2】

    Pandas数据查询的5种方法: 数据准备: import pandas as pd path = 'C:\\Users\\zhang\\Desktop\\ant-learn-pandas-maste ...

  10. C# xml与对象相互转换

    例如: 1.对象转xml(对象序列化为xml) string strImage= XmlSerializeHelper.Serialize<List<ImageSingle>> ...