Loading

图最短路径之BellmanFord

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 次。

贝尔曼-福特算法示例图 1

让所有边按以下顺序进行处理:(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) 的时间。

贝尔曼-福特算法示例图 2

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

贝尔曼-福特算法示例图 3

第二次迭代保证给出所有最短路径,这些路径的长度最多为 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);
  }
}

 

posted @ 2021-08-29 15:22  Philosophy  阅读(307)  评论(0编辑  收藏  举报