Johnson 全源最短路径算法

解决单源最短路径问题(Single Source Shortest Paths Problem)的算法包括:

  • Dijkstra 单源最短路径算法:时间复杂度为 O(E + VlogV),要求权值非负;
  • Bellman-Ford 单源最短路径算法:时间复杂度为 O(VE),适用于带负权值情况;

对于全源最短路径问题(All-Pairs Shortest Paths Problem),可以认为是单源最短路径问题的推广,即分别以每个顶点作为源顶点并求其至其它顶点的最短距离。例如,对每个顶点应用 Bellman-Ford 算法,则可得到所有顶点间的最短路径的运行时间为 O(V2E),由于图中顶点都是连通的,而边的数量可能会比顶点更多,这个时间没有比 Floyd-Warshall 全源最短路径算法 O(V3) 更优。那么,再试下对每个顶点应用 Dijkstra 算法,则可得到所有顶点间的最短路径的运行时间为 O(VE + V2logV),看起来优于 Floyd-Warshall 算法的 O(V3),所以看起来使用基于 Dijkstra 算法的改进方案好像更好,但问题是 Dijkstra 算法要求图中所有边的权值非负,不适合通用的情况。

在 1977 年,Donald B. Johnson 提出了对所有边的权值进行 "re-weight" 的算法,使得边的权值非负,进而可以使用 Dijkstra 算法进行最短路径的计算。

我们先自己思考下如何进行 "re-weight" 操作,比如,简单地对每条边的权值加上一个较大的正数,使其非负,是否可行?

   1     1     1
s-----a-----b-----c\              /\          /\______/4

比如上面的图中,共 4 条边,权值分别为 1,1,1,4。当前 s --> c 的最短路径是 {s-a, a-b, b-c} 即 1+1+1=3。而如果将所有边的权值加 1,则最短路径就会变成 {s-c} 的 5,因为 2+2+2=6,实际上导致了最短路径的变化,显然是错误的。

那么,Johnson 算法是如何对边的权值进行 "re-weight" 的呢?以下面的图 G 为例,有 4 个顶点和 5 条边。

首先,新增一个源顶点 4,并使其与所有顶点连通,新边赋权值为 0,如下图所示。

使用 Bellman-Ford 算法 计算新的顶点到所有其它顶点的最短路径,则从 4 至 {0, 1, 2, 3} 的最短路径分别是 {0, -5, -1, 0}。即有 h[] = {0, -5, -1, 0}。当得到这个 h[] 信息后,将新增的顶点 4 移除,然后使用如下公式对所有边的权值进行 "re-weight":

w(u, v) = w(u, v) + (h[u] - h[v]).

则可得到下图中的结果:

此时,所有边的权值已经被 "re-weight" 为非负。此时,就可以利用 Dijkstra 算法对每个顶点分别进行最短路径的计算了。

Johnson 算法描述如下:

  1. 给定图 G = (V, E),增加一个新的顶点 s,使 s 指向图 G 中的所有顶点都建立连接,设新的图为 G’;
  2. 对图 G’ 中顶点 s 使用 Bellman-Ford 算法计算单源最短路径,得到结果 h[] = {h[0], h[1], .. h[V-1]};
  3. 对原图 G 中的所有边进行 "re-weight",即对于每个边 (u, v),其新的权值为 w(u, v) + (h[u] - h[v]);
  4. 移除新增的顶点 s,对每个顶点运行 Dijkstra 算法求得最短路径;

Johnson 算法的运行时间为 O(V2logV + VE)。

Johnson 算法伪码实现如下:

Johnson 算法 C# 代码实现如下:

复制代码
  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 
  5 namespace GraphAlgorithmTesting
  6 {
  7   class Program
  8   {
  9     static void Main(string[] args)
 10     {
 11       // build a directed and negative weighted graph
 12       Graph directedGraph1 = new Graph(5);
 13       directedGraph1.AddEdge(0, 1, -1);
 14       directedGraph1.AddEdge(0, 2, 4);
 15       directedGraph1.AddEdge(1, 2, 3);
 16       directedGraph1.AddEdge(1, 3, 2);
 17       directedGraph1.AddEdge(1, 4, 2);
 18       directedGraph1.AddEdge(3, 2, 5);
 19       directedGraph1.AddEdge(3, 1, 1);
 20       directedGraph1.AddEdge(4, 3, -3);
 21 
 22       Console.WriteLine();
 23       Console.WriteLine("Graph Vertex Count : {0}", directedGraph1.VertexCount);
 24       Console.WriteLine("Graph Edge Count : {0}", directedGraph1.EdgeCount);
 25       Console.WriteLine();
 26 
 27       int[,] distSet1 = directedGraph1.Johnsons();
 28       PrintSolution(directedGraph1, distSet1);
 29 
 30       // build a directed and positive weighted graph
 31       Graph directedGraph2 = new Graph(4);
 32       directedGraph2.AddEdge(0, 1, 5);
 33       directedGraph2.AddEdge(0, 3, 10);
 34       directedGraph2.AddEdge(1, 2, 3);
 35       directedGraph2.AddEdge(2, 3, 1);
 36 
 37       Console.WriteLine();
 38       Console.WriteLine("Graph Vertex Count : {0}", directedGraph2.VertexCount);
 39       Console.WriteLine("Graph Edge Count : {0}", directedGraph2.EdgeCount);
 40       Console.WriteLine();
 41 
 42       int[,] distSet2 = directedGraph2.Johnsons();
 43       PrintSolution(directedGraph2, distSet2);
 44 
 45       Console.ReadKey();
 46     }
 47 
 48     private static void PrintSolution(Graph g, int[,] distSet)
 49     {
 50       Console.Write("\t");
 51       for (int i = 0; i < g.VertexCount; i++)
 52       {
 53         Console.Write(i + "\t");
 54       }
 55       Console.WriteLine();
 56       Console.Write("\t");
 57       for (int i = 0; i < g.VertexCount; i++)
 58       {
 59         Console.Write("-" + "\t");
 60       }
 61       Console.WriteLine();
 62       for (int i = 0; i < g.VertexCount; i++)
 63       {
 64         Console.Write(i + "|\t");
 65         for (int j = 0; j < g.VertexCount; j++)
 66         {
 67           if (distSet[i, j] == int.MaxValue)
 68           {
 69             Console.Write("INF" + "\t");
 70           }
 71           else
 72           {
 73             Console.Write(distSet[i, j] + "\t");
 74           }
 75         }
 76         Console.WriteLine();
 77       }
 78     }
 79 
 80     class Edge
 81     {
 82       public Edge(int begin, int end, int weight)
 83       {
 84         this.Begin = begin;
 85         this.End = end;
 86         this.Weight = weight;
 87       }
 88 
 89       public int Begin { get; private set; }
 90       public int End { get; private set; }
 91       public int Weight { get; private set; }
 92 
 93       public void Reweight(int newWeight)
 94       {
 95         this.Weight = newWeight;
 96       }
 97 
 98       public override string ToString()
 99       {
100         return string.Format(
101           "Begin[{0}], End[{1}], Weight[{2}]",
102           Begin, End, Weight);
103       }
104     }
105 
106     class Graph
107     {
108       private Dictionary<int, List<Edge>> _adjacentEdges
109         = new Dictionary<int, List<Edge>>();
110 
111       public Graph(int vertexCount)
112       {
113         this.VertexCount = vertexCount;
114       }
115 
116       public int VertexCount { get; private set; }
117 
118       public int EdgeCount
119       {
120         get
121         {
122           return _adjacentEdges.Values.SelectMany(e => e).Count();
123         }
124       }
125 
126       public void AddEdge(int begin, int end, int weight)
127       {
128         if (!_adjacentEdges.ContainsKey(begin))
129         {
130           var edges = new List<Edge>();
131           _adjacentEdges.Add(begin, edges);
132         }
133 
134         _adjacentEdges[begin].Add(new Edge(begin, end, weight));
135       }
136 
137       public void AddEdge(Edge edge)
138       {
139         AddEdge(edge.Begin, edge.End, edge.Weight);
140       }
141 
142       public void AddEdges(IEnumerable<Edge> edges)
143       {
144         foreach (var edge in edges)
145         {
146           AddEdge(edge);
147         }
148       }
149 
150       public IEnumerable<Edge> GetAllEdges()
151       {
152         return _adjacentEdges.Values.SelectMany(e => e);
153       }
154 
155       public int[,] Johnsons()
156       {
157         // distSet[,] will be the output matrix that will finally have the shortest 
158         // distances between every pair of vertices
159         int[,] distSet = new int[VertexCount, VertexCount];
160 
161         for (int i = 0; i < VertexCount; i++)
162         {
163           for (int j = 0; j < VertexCount; j++)
164           {
165             distSet[i, j] = int.MaxValue;
166           }
167         }
168         for (int i = 0; i < VertexCount; i++)
169         {
170           distSet[i, i] = 0;
171         }
172 
173         // step 1: add new vertex s and connect to all vertices
174         Graph g = new Graph(this.VertexCount + 1);
175         g.AddEdges(this.GetAllEdges());
176 
177         int s = this.VertexCount;
178         for (int i = 0; i < this.VertexCount; i++)
179         {
180           g.AddEdge(s, i, 0);
181         }
182 
183         // step 2: use Bellman-Ford to evaluate shortest paths from s
184         int[] h = g.BellmanFord(s);
185 
186         // step 3: re-weighting edges of the original graph
187         //         w(u, v) = w(u, v) + (h[u] - h[v])
188         foreach (var edge in this.GetAllEdges())
189         {
190           edge.Reweight(edge.Weight + (h[edge.Begin] - h[edge.End]));
191         }
192 
193         // step 4: use Dijkstra for each edges
194         for (int begin = 0; begin < this.VertexCount; begin++)
195         {
196           int[] dist = this.Dijkstra(begin);
197           for (int end = 0; end < dist.Length; end++)
198           {
199             if (dist[end] != int.MaxValue)
200             {
201               distSet[begin, end] = dist[end] - (h[begin] - h[end]);
202             }
203           }
204         }
205 
206         return distSet;
207       }
208 
209       public int[,] FloydWarshell()
210       {
211         /* distSet[,] will be the output matrix that will finally have the shortest 
212            distances between every pair of vertices */
213         int[,] distSet = new int[VertexCount, VertexCount];
214 
215         for (int i = 0; i < VertexCount; i++)
216         {
217           for (int j = 0; j < VertexCount; j++)
218           {
219             distSet[i, j] = int.MaxValue;
220           }
221         }
222         for (int i = 0; i < VertexCount; i++)
223         {
224           distSet[i, i] = 0;
225         }
226 
227         /* Initialize the solution matrix same as input graph matrix. Or 
228            we can say the initial values of shortest distances are based
229            on shortest paths considering no intermediate vertex. */
230         foreach (var edge in _adjacentEdges.Values.SelectMany(e => e))
231         {
232           distSet[edge.Begin, edge.End] = edge.Weight;
233         }
234 
235         /* Add all vertices one by one to the set of intermediate vertices.
236           ---> Before start of a iteration, we have shortest distances between all
237           pairs of vertices such that the shortest distances consider only the
238           vertices in set {0, 1, 2, .. k-1} as intermediate vertices.
239           ---> After the end of a iteration, vertex no. k is added to the set of
240           intermediate vertices and the set becomes {0, 1, 2, .. k} */
241         for (int k = 0; k < VertexCount; k++)
242         {
243           // Pick all vertices as source one by one
244           for (int i = 0; i < VertexCount; i++)
245           {
246             // Pick all vertices as destination for the above picked source
247             for (int j = 0; j < VertexCount; j++)
248             {
249               // If vertex k is on the shortest path from
250               // i to j, then update the value of distSet[i,j]
251               if (distSet[i, k] != int.MaxValue
252                 && distSet[k, j] != int.MaxValue
253                 && distSet[i, k] + distSet[k, j] < distSet[i, j])
254               {
255                 distSet[i, j] = distSet[i, k] + distSet[k, j];
256               }
257             }
258           }
259         }
260 
261         return distSet;
262       }
263 
264       public int[] BellmanFord(int source)
265       {
266         // distSet[i] will hold the shortest distance from source to i
267         int[] distSet = new int[VertexCount];
268 
269         // Step 1: Initialize distances from source to all other vertices as INFINITE
270         for (int i = 0; i < VertexCount; i++)
271         {
272           distSet[i] = int.MaxValue;
273         }
274         distSet[source] = 0;
275 
276         // Step 2: Relax all edges |V| - 1 times. A simple shortest path from source
277         // to any other vertex can have at-most |V| - 1 edges
278         for (int i = 1; i <= VertexCount - 1; i++)
279         {
280           foreach (var edge in _adjacentEdges.Values.SelectMany(e => e))
281           {
282             int u = edge.Begin;
283             int v = edge.End;
284             int weight = edge.Weight;
285 
286             if (distSet[u] != int.MaxValue
287               && distSet[u] + weight < distSet[v])
288             {
289               distSet[v] = distSet[u] + weight;
290             }
291           }
292         }
293 
294         // Step 3: check for negative-weight cycles.  The above step guarantees
295         // shortest distances if graph doesn't contain negative weight cycle.
296         // If we get a shorter path, then there is a cycle.
297         foreach (var edge in _adjacentEdges.Values.SelectMany(e => e))
298         {
299           int u = edge.Begin;
300           int v = edge.End;
301           int weight = edge.Weight;
302 
303           if (distSet[u] != int.MaxValue
304             && distSet[u] + weight < distSet[v])
305           {
306             Console.WriteLine("Graph contains negative weight cycle.");
307           }
308         }
309 
310         return distSet;
311       }
312 
313       public int[] Dijkstra(int source)
314       {
315         // dist[i] will hold the shortest distance from source to i
316         int[] distSet = new int[VertexCount];
317 
318         // sptSet[i] will true if vertex i is included in shortest
319         // path tree or shortest distance from source to i is finalized
320         bool[] sptSet = new bool[VertexCount];
321 
322         // initialize all distances as INFINITE and stpSet[] as false
323         for (int i = 0; i < VertexCount; i++)
324         {
325           distSet[i] = int.MaxValue;
326           sptSet[i] = false;
327         }
328 
329         // distance of source vertex from itself is always 0
330         distSet[source] = 0;
331 
332         // find shortest path for all vertices
333         for (int i = 0; i < VertexCount - 1; i++)
334         {
335           // pick the minimum distance vertex from the set of vertices not
336           // yet processed. u is always equal to source in first iteration.
337           int u = CalculateMinDistance(distSet, sptSet);
338 
339           // mark the picked vertex as processed
340           sptSet[u] = true;
341 
342           // update dist value of the adjacent vertices of the picked vertex.
343           for (int v = 0; v < VertexCount; v++)
344           {
345             // update dist[v] only if is not in sptSet, there is an edge from 
346             // u to v, and total weight of path from source to  v through u is 
347             // smaller than current value of dist[v]
348             if (!sptSet[v]
349               && distSet[u] != int.MaxValue
350               && _adjacentEdges.ContainsKey(u)
351               && _adjacentEdges[u].Exists(e => e.End == v))
352             {
353               int d = _adjacentEdges[u].Single(e => e.End == v).Weight;
354               if (distSet[u] + d < distSet[v])
355               {
356                 distSet[v] = distSet[u] + d;
357               }
358             }
359           }
360         }
361 
362         return distSet;
363       }
364 
365       private int CalculateMinDistance(int[] distSet, bool[] sptSet)
366       {
367         int minDistance = int.MaxValue;
368         int minDistanceIndex = -1;
369 
370         for (int v = 0; v < VertexCount; v++)
371         {
372           if (!sptSet[v] && distSet[v] <= minDistance)
373           {
374             minDistance = distSet[v];
375             minDistanceIndex = v;
376           }
377         }
378 
379         return minDistanceIndex;
380       }
381     }
382   }
383 }
复制代码

运行结果如下:

参考资料

  • 广度优先搜索
  • 深度优先搜索
  • Breadth First Traversal for a Graph
  • Depth First Traversal for a Graph
  • Dijkstra 单源最短路径算法
  • Bellman-Ford 单源最短路径算法
  • Bellman–Ford algorithm
  • Introduction To Algorithm
  • Floyd-Warshall's algorithm
  • Bellman-Ford algorithm for single-source shortest paths
  • Dynamic Programming | Set 23 (Bellman–Ford Algorithm)
  • Dynamic Programming | Set 16 (Floyd Warshall Algorithm)
  • Johnson’s algorithm for All-pairs shortest paths
  • Floyd-Warshall's algorithm
  • 最短路径算法--Dijkstra算法,Bellmanford算法,Floyd算法,Johnson算法
  • QuickGraph, Graph Data Structures And Algorithms for .NET
  • CHAPTER 26: ALL-PAIRS SHORTEST PATHS





本文转自匠心十年博客园博客,原文链接:http://www.cnblogs.com/gaochundong/p/johnson_algorithm.html,如需转载请自行联系原作者


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/453421.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Machine Learning 学习笔记1 - 基本概念以及各分类

What is machine learning? 并没有广泛认可的定义来准确定义机器学习。以下定义均为译文&#xff0c;若以后有时间&#xff0c;将补充原英文...... 定义1、来自Arthur Samuel&#xff08;上世纪50年代、西洋棋程序&#xff09; 在进行特定编程的情况下给予计算机学习能力的领域…

蒙特 卡罗方法matlab,蒙特·卡罗方法中的数学之美,你一定不想错过

原标题&#xff1a;蒙特卡罗方法中的数学之美&#xff0c;你一定不想错过有方教育——我们致力于为中学生提供学界和业界前沿的学术科研教育内容&#xff0c;帮助学生参加海外科研项目&#xff0c;在提升申请竞争力的同时&#xff0c;获得领跑优势。一、概述蒙特卡罗方法(Monte…

【 CDN 最佳实践】CDN 命中率优化思路

CDN 在静态资源的加速场景中是将静态资源缓存在距离客户端较近的CDN 节点上&#xff0c;然后客户端访问该资源即可通过较短的链路直接从缓存中获取资源&#xff0c;而避免再通过较长的链路回源获取静态资源。因此 CDN的缓存命中率的高低直接影响客户体验&#xff0c;而保证较高…

Python基础-time and datetime

一、在Python中&#xff0c;通常有这几种方式来表示时间&#xff1a; 时间戳格式化的时间字符串元组&#xff08;struct_time&#xff09;共九个元素。由于Python的time模块实现主要调用C库&#xff0c;所以各个平台可能有所不同。1.时间戳&#xff08;timestamp&#xff09;的…

matlab中欧姆如何表示,在excel中欧姆符号怎么打

在excel中欧姆符号怎么打&#xff0c;相信对于好多熟练用excel的朋友来说&#xff0c;是很简单不过的&#xff0c;但是对于有些初学者来说&#xff0c;就是菜鸟啦&#xff0c;就有点懵懵懂懂的感觉了&#xff0c;毕竟刚接触的东西还没用过嘛。但是&#xff0c;没关系今天笔者就…

zookeeper伪集群(在一台机器上集群)

2019独角兽企业重金招聘Python工程师标准>>> 创建一下的目录结构zookeeper-3.4.10是你下载的zookeeper的解压包 /zookeeper_cluster----/server_one|---/data|myid(文件)|---/datalog|---/zookeeper-3.4.10|---/bin|---/conf|---zoo.cfg|---..... |---/....----/ser…

Spring核心接口之Ordered

一、Ordered接口介绍Spring中提供了一个Ordered接口。从单词意思就知道Ordered接口的作用就是用来排序的。Spring框架是一个大量使用策略设计模式的框架&#xff0c;这意味着有很多相同接口的实现类&#xff0c;那么必定会有优先级的问题。于是Spring就提供了Ordered这个接口&a…

将本地代码上传至github

注册github账号 https://github.com/ 安装git工具 https://git-for-windows.github.io 1.在github中创建一个项目 2.填写相应信息&#xff0c;点击create Repository name: 仓库名称 Description(可选): 仓库描述介绍 Public, Private : 仓库权限&#xff08;公开共享&#xff…

Java二十三设计模式之------工厂方法模式

一、工厂方法模式&#xff08;Factory Method&#xff09; 工厂方法模式有三种 1、普通工厂模式&#xff1a;就是建立一个工厂类&#xff0c;对实现了同一接口的一些类进行实例的创建。首先看下关系图&#xff1a; 举例如下&#xff1a;&#xff08;我们举一个发送邮件和短信的…

一元多项式乘法算法

我认为大致算法应该是这样的: 首先准备一个空的链表L。利用第一个多项式的的指针所指的节点数值乘以多项式二的每一项&#xff0c;将结果保存在链表L中。 然后将指向该节点的指针后移到下一个节点继续进行乘法运算&#xff0c;将所得结果加到L中&#xff08;这个操作已经在一…

Oracle 基础

为什么80%的码农都做不了架构师&#xff1f;>>> Oracle DB笔录&#xff0c;以后会不断Add&#xff0c;欢迎留言补充! --cmd.exe(你懂得!) --[1]多个数据库实例&#xff0c;切换选择DB后&#xff0c;登录操作 set ORACLE_SIDorcl --选择DB orcl(你的DB实例名) --可在…

Linux执行命令提示Password,linux expect远程自动登录以及执行命令

linux远程自动登录以及执行命令远程登录该自动登录的过程是通过shell里面expect实现的&#xff0c;类似相当于开了一个类似于cmd的命令段输出IP和密码。注意该脚本能够执行的前提是安装了expectyum install -y expect直接上脚本&#xff1a;#!/usr/bin/expect …

MyBatis 在xml文件中处理大于号小于号的方法

为什么80%的码农都做不了架构师&#xff1f;>>> 第一种方法&#xff1a;用转义字符&#xff08;注&#xff1a;对大小写敏感&#xff01; &#xff09; 用了转义字符把>和<替换掉&#xff0c;然后就没有问题了。 SELECT * FROM test WHERE 1 1 AND start_da…

linux 进程间读写锁,Linux系统编程—进程间同步

我们知道&#xff0c;线程间同步有多种方式&#xff0c;比如&#xff1a;信号量、互斥量、读写锁&#xff0c;等等。那进程间如何实现同步呢&#xff1f;本文介绍两种方式&#xff1a;互斥量和文件锁。##互斥量mutex我们已经知道了互斥量可以用于在线程间同步&#xff0c;但实际…

属性依赖注入

1.依赖注入方法 手动装配和自动装配 2.手动装配 2.1 基于xml装配 2.1.1 构造方法 <!-- 构造方法注入<constructor-arg>name:参数名type:类型value: --> <bean id"user" class"g_xml.constructor.User"><constructor-arg name"id…

MediaWiki安装配置(Linux)【转】

阅读目录 2.1 本例子的安装环境如下&#xff1a;转自&#xff1a;http://blog.csdn.net/gao36951/article/details/43965527 版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。 目录(?)[-] 1MediaWiki简介 2MediaWiki安装21 本例子的安装环境如…

在Asp.net core返回PushStream

最近用asp.net core webapi实现了一个实时视频流的推送功能&#xff0c;在Asp.net中&#xff0c;这个是通过PushStreamContent来实现的。 基于对asp.net core的知识&#xff0c;随手写了一个&#xff08;要求控制器继承自Controller基类&#xff09; [HttpGet] public async Ta…

顺序栈的代码实现

栈是一种限定只在表尾进行插入或删除操作的线性表&#xff0c;栈也是线性表。表头称为栈的底部,表尾称为栈的顶部,表为空称为空栈。 栈又称为后进先出的线性表,栈也有两种表示:顺序栈与链式栈。顺序栈是利用一组地址连续的存储单元。依次存放从栈底到栈顶的数据元素。 #includ…

eclipse+tomcat开发web程序

环境&#xff1a;windows 7Eclipse Java EE IDE for Web Developerstomcat 7.02 插件&#xff1a;tomcatPluginV321.zip 一.配置Tomcat插件 我们创建一个myplugins文件夹用于存放插件&#xff0c;myplugins位于D:/Program Files/J2EE目录下。eclipse安装路径为&#xff1a;D:/P…

LoadRunner参数包含逗号

loadrunner的参数以逗号区分&#xff0c; 如果参数本身包含逗号&#xff0c;则会报错 使用","将逗号包起来即可&#xff0c;如下图 转载于:https://www.cnblogs.com/cherrysu/p/8507649.html