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. C++里也有菱形运算符?

    最近在翻<c++函数式编程>的时候看到有一小节在说c++14新增了"菱形运算符".我寻思c++里好像没什么运算符叫这名字啊,而且c++14新增的功能很少,我也不记得有添 ...

  2. neovim 使用系统剪贴板

    neovim 使用系统剪贴板 1.vim 与 neovim 使用系统剪切板的不同 Nvim has no direct connection to the system clipboard. Inst ...

  3. C#/C++ 通过ODBC连接OceanBase Oracle租户

    概述 近期我们项目正处于将Oracle数据库迁移到OceanBase Oracle租户模式的阶段.考虑到我们项目采用了C++和C#混合开发,并且使用了多种技术,因此存在多种数据库连接方式.然而,针对C ...

  4. docker-compose 安装redis

    一. docker 拉去最新版本的redis `docker pull redis:6.0.6` #后面可以带上tag号, 默认拉取最新版本 二. docker安装redis 执行命令: mkdir ...

  5. LVS负载均衡(2)-- NAT模型搭建实例

    目录 1. LVS NAT模型搭建 1.1 NAT模型网络规划 1.2 NAT模型访问流程 1.3 NAT模型配置步骤 1.3.1 ROUTER设备配置 1.3.2 后端nginx服务器配置 1.3. ...

  6. linux文本三剑客之grep及正则表达式详解

    linux文本三剑客之grep及正则表达式详解 目录 linux文本三剑客之grep及正则表达式详解 1. grep命令详解 2. 正则表达式 2.1 基本正则表达式 2.2 扩展正则表达式 1. g ...

  7. 学习c# 7.0-7.3的ref、fixed特性并在Unity下测试

    1.ref的一些运用 1.1 ref readonly 关于ref,一个主要应用是防止结构体拷贝,若返回的结构体不需要修改则用ref readonly,类似c++的const标记 : private ...

  8. Linux环境下:程序的链接, 装载和库[可执行文件的装载]

    现代操作系统如何装载可执行文件? 给进程分配独立的虚拟地址空间 建立虚拟地址空间和可执行文件的映射关系 把CPU指令寄存器设置成可执行文件的入口地址,启动执行 可执行文件在装载的过程中实际上是映射的虚 ...

  9. 4G EPS 的网络协议栈

    目录 文章目录 目录 前文列表 LTE/EPC 协议栈 E-UTRAN 空中接口协议栈 用户平面协议 PHY 层 MAC 层 RLC 层 PDCP 层 控制平面协议 RRC 层 NAS 层 EPC 核 ...

  10. docker/k8s常见错误处理

    启动docker失败,报错了 启动docker失败,报错了.Failed to load environment files: No such file or directory [root@mcwk ...