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. 快速部署 微软开源的 Garnet 键值数据库

    快速部署 微软开源的 Garnet 键值数据库 Garnet 是 Microsoft Research 推出的一种新型远程缓存存储,其设计速度极快.可扩展且延迟低. Garnet 在单个节点内是线程可 ...

  2. 通过Ingress-nginx实现灰度发布---灰度发布(22)

    1.通过Ingress-nginx实现灰度发布 场景一: 将新版本灰度给部分用户 假设线上运行了一套对外提供 7 层服务的 Service A 服务,后来开发了个新版本 Service A' 想 要上 ...

  3. C++编程英语词汇

    abstract抽象的 abstraction抽象性.抽象件 access访问 access level访问级别 access function访问函数 adapter适配器 address地址 ad ...

  4. AnaTraf 网络万用表流量分析教程系列 | AnaTraf 网络万用表 B 站频道

    为了更好的向大家分享如何使用 AnaTraf 网络万用表进行网络流量分析.网络故障排除,AnaTraf 开通了 B 站频道. 在 B 站上,将以视频的形式向大家介绍如何使用 AnaTraf 网络万用表 ...

  5. Splashtop Business Access 的常见问题解答

    Splashtop Business Access 是一款优秀的远程访问软件,使个人和团队可以快速.简单.安全地访问远程计算机.Splashtop Business Access 是 LogMeIn ...

  6. C 语言编程 — 逻辑控制语句

    目录 文章目录 目录 前文列表 结构化程序设计 条件分支语句 if/else 语句 if 语句 if/else 语句 if/else-if/else 语句 嵌套 if 语句 switch 语句 swi ...

  7. npm包离线安装

    npm包离线安装 npm包的安装,在Internet联网机器上通过npm install轻松搞定的事情,在离线或者纯内网环境下就变得异常艰难,本文就来讲一讲离线安装npm包的方法. 通过 npm in ...

  8. Flyway简单迁移失败问题

    因为我是初学者,所以问题 只是一些细节性问题,现在看看V开头创建数据库迁移时的错误 问题一: flyway 命名规则问题这个绝对的细节中的细节问题 问题一解决方案: 命名规则:一定要遵循flyway的 ...

  9. 🔥 FolkMQ v1.5.1 发布(“新式” 国产消息中间件)

    FolkMQ 是个"新式"的消息中间件.强调:"小而巧"."简而强". 功能简表 角色 功能 生产者(客户端) 发布普通消息.Qos0消息. ...

  10. 【论文笔记】轻量级网络MobileNet

    [深度学习]总目录 MobileNet V1:<MobileNets: Efficient Convolutional Neural Networks for MobileVision Appl ...