|
1 |
| -/*** |
2 |
| - * Computes Dijkstra's Shortest-Paths for Directed Weighted Graphs from a single-source to all destinations. |
3 |
| - * This class provides the same API as the BreadthFirstShortestPaths<T>. |
4 |
| - */ |
5 |
| - |
6 |
| -using Algorithms.Common; |
7 |
| -using DataStructures.Graphs; |
| 1 | +using DataStructures.Graphs; |
8 | 2 | using DataStructures.Heaps;
|
9 | 3 | using System;
|
10 | 4 | using System.Collections.Generic;
|
| 5 | +using System.Linq; |
11 | 6 |
|
12 | 7 | namespace Algorithms.Graphs
|
13 | 8 | {
|
| 9 | + /// <summary> |
| 10 | + /// Computes Dijkstra's Shortest-Paths for Directed Weighted Graphs from a single-source to all destinations. |
| 11 | + /// </summary> |
14 | 12 | public class DijkstraShortestPaths<TGraph, TVertex>
|
15 | 13 | where TGraph : IGraph<TVertex>, IWeightedGraph<TVertex>
|
16 | 14 | where TVertex : IComparable<TVertex>
|
17 | 15 | {
|
18 |
| - /// <summary> |
19 |
| - /// INSTANCE VARIABLES |
20 |
| - /// </summary> |
21 |
| - private int _edgesCount; |
22 |
| - private int _verticesCount; |
| 16 | + private const long Infinity = long.MaxValue; |
| 17 | + private const int NilPredecessor = -1; |
| 18 | + |
23 | 19 | private long[] _distances;
|
24 | 20 | private int[] _predecessors;
|
25 |
| - private WeightedEdge<TVertex>[] _edgeTo; |
26 | 21 |
|
27 |
| - // A dictionary that maps node-values to integer indeces |
28 | 22 | private Dictionary<TVertex, int> _nodesToIndices;
|
29 |
| - |
30 |
| - // A dictionary that maps integer index to node-value |
31 | 23 | private Dictionary<int, TVertex> _indicesToNodes;
|
32 | 24 |
|
33 |
| - // A const that represent an infinite distance |
34 |
| - private const long Infinity = long.MaxValue; |
35 |
| - private const int NilPredecessor = -1; |
| 25 | + private MinPriorityQueue<TVertex, long> _minPriorityQueue; |
36 | 26 |
|
| 27 | + private readonly TGraph _graph; |
| 28 | + private readonly TVertex _source; |
37 | 29 |
|
38 |
| - /// <summary> |
39 |
| - /// CONSTRUCTOR |
40 |
| - /// </summary> |
41 |
| - /// <param name="Graph"></param> |
42 |
| - public DijkstraShortestPaths(TGraph Graph, TVertex Source) |
| 30 | + public DijkstraShortestPaths(TGraph graph, TVertex source) |
43 | 31 | {
|
44 |
| - if (Graph == null) |
45 |
| - { |
46 |
| - throw new ArgumentNullException(); |
47 |
| - } |
| 32 | + if (graph == null) |
| 33 | + throw new ArgumentNullException(nameof(graph)); |
48 | 34 |
|
49 |
| - if (!Graph.HasVertex(Source)) |
50 |
| - { |
| 35 | + if (source == null) |
| 36 | + throw new ArgumentNullException(nameof(source)); |
| 37 | + |
| 38 | + if (!graph.HasVertex(source)) |
51 | 39 | throw new ArgumentException("The source vertex doesn't belong to graph.");
|
52 |
| - } |
53 | 40 |
|
54 |
| - // Init |
55 |
| - _initializeDataMembers(Graph); |
| 41 | + if (graph.Edges.Any(edge => edge.Weight < 0)) |
| 42 | + throw new ArgumentException("Negative edge weight detected."); |
56 | 43 |
|
57 |
| - // Traverse the graph |
58 |
| - _dijkstra(Graph, Source); |
| 44 | + _graph = graph; |
| 45 | + _source = source; |
59 | 46 |
|
60 |
| - // check for the acyclic invariant |
61 |
| - if (!_checkOptimalityConditions(Graph, Source)) |
62 |
| - { |
63 |
| - throw new InvalidOperationException("Graph doesn't match optimality condition."); |
64 |
| - } |
| 47 | + _initialize(); |
| 48 | + _dijkstra(); |
65 | 49 | }
|
66 | 50 |
|
67 |
| - |
68 |
| - /************************************************************************************************************/ |
69 |
| - |
70 |
| - |
71 | 51 | /// <summary>
|
72 |
| - /// The Dijkstra's algorithm. |
| 52 | + /// The Dijkstra's algorithm. |
73 | 53 | /// </summary>
|
74 |
| - private void _dijkstra(TGraph graph, TVertex source) |
| 54 | + private void _dijkstra() |
75 | 55 | {
|
76 |
| - var minPQ = new MinPriorityQueue<TVertex, long>((uint)_verticesCount); |
77 |
| - |
78 |
| - var srcIndex = _nodesToIndices[source]; |
79 |
| - _distances[srcIndex] = 0; |
80 |
| - |
81 |
| - minPQ.Enqueue(source, _distances[srcIndex]); |
82 |
| - |
83 |
| - // Main loop |
84 |
| - while (!minPQ.IsEmpty) |
| 56 | + while (!_minPriorityQueue.IsEmpty) |
85 | 57 | {
|
86 |
| - var current = minPQ.DequeueMin(); // get vertex with min weight |
87 |
| - var currentIndex = _nodesToIndices[current]; // get its index |
88 |
| - var edges = graph.OutgoingEdges(current) as IEnumerable<WeightedEdge<TVertex>>; // get its outgoing weighted edges |
| 58 | + var currentVertex = _minPriorityQueue.DequeueMin(); |
| 59 | + var currentVertexIndex = _nodesToIndices[currentVertex]; |
89 | 60 |
|
90 |
| - foreach (var edge in edges) |
| 61 | + var outgoingEdges = _graph.OutgoingEdges(currentVertex); |
| 62 | + foreach (var outgoingEdge in outgoingEdges) |
91 | 63 | {
|
92 |
| - var adjacentIndex = _nodesToIndices[edge.Destination]; |
93 |
| - |
94 |
| - // calculate a new possible weighted path if the edge weight is less than infinity |
95 |
| - var delta = Infinity; |
96 |
| - if (edge.Weight < Infinity && Infinity - edge.Weight > _distances[currentIndex]) // Handles overflow |
97 |
| - { |
98 |
| - delta = _distances[currentIndex] + edge.Weight; |
99 |
| - } |
| 64 | + var adjacentIndex = _nodesToIndices[outgoingEdge.Destination]; |
| 65 | + var delta = _distances[currentVertexIndex] != Infinity ? _distances[currentVertexIndex] + outgoingEdge.Weight : Infinity; |
100 | 66 |
|
101 |
| - // Relax the edge |
102 |
| - // if check is true, a shorter path is found from current to adjacent |
103 | 67 | if (delta < _distances[adjacentIndex])
|
104 | 68 | {
|
105 |
| - _edgeTo[adjacentIndex] = edge; |
106 | 69 | _distances[adjacentIndex] = delta;
|
107 |
| - _predecessors[adjacentIndex] = currentIndex; |
| 70 | + _predecessors[adjacentIndex] = currentVertexIndex; |
108 | 71 |
|
109 |
| - // decrease priority with a new distance if it exists; otherwise enqueque it |
110 |
| - if (minPQ.Contains(edge.Destination)) |
| 72 | + if (_minPriorityQueue.Contains(outgoingEdge.Destination)) |
111 | 73 | {
|
112 |
| - minPQ.UpdatePriority(edge.Destination, delta); |
| 74 | + _minPriorityQueue.UpdatePriority(outgoingEdge.Destination, delta); |
113 | 75 | }
|
114 | 76 | else
|
115 | 77 | {
|
116 |
| - minPQ.Enqueue(edge.Destination, delta); |
| 78 | + _minPriorityQueue.Enqueue(outgoingEdge.Destination, delta); |
117 | 79 | }
|
118 | 80 | }
|
119 |
| - }//end-foreach |
120 |
| - }//end-while |
121 |
| - } |
122 |
| - |
123 |
| - /// <summary> |
124 |
| - /// Constructors helper function. Initializes some of the data memebers. |
125 |
| - /// </summary> |
126 |
| - private void _initializeDataMembers(TGraph Graph) |
127 |
| - { |
128 |
| - _edgesCount = Graph.EdgesCount; |
129 |
| - _verticesCount = Graph.VerticesCount; |
130 |
| - |
131 |
| - _distances = new long[_verticesCount]; |
132 |
| - _predecessors = new int[_verticesCount]; |
133 |
| - _edgeTo = new WeightedEdge<TVertex>[_edgesCount]; |
134 |
| - |
135 |
| - _nodesToIndices = new Dictionary<TVertex, int>(); |
136 |
| - _indicesToNodes = new Dictionary<int, TVertex>(); |
137 |
| - |
138 |
| - // Reset the information arrays |
139 |
| - var i = 0; |
140 |
| - foreach (var node in Graph.Vertices) |
141 |
| - { |
142 |
| - if (i >= _verticesCount) |
143 |
| - { |
144 |
| - break; |
145 | 81 | }
|
146 |
| - |
147 |
| - _edgeTo[i] = null; |
148 |
| - _distances[i] = Infinity; |
149 |
| - _predecessors[i] = NilPredecessor; |
150 |
| - |
151 |
| - _nodesToIndices.Add(node, i); |
152 |
| - _indicesToNodes.Add(i, node); |
153 |
| - |
154 |
| - ++i; |
155 | 82 | }
|
156 | 83 | }
|
157 | 84 |
|
158 |
| - /// <summary> |
159 |
| - /// Constructors helper function. Checks Optimality Conditions: |
160 |
| - /// (i) for all edges e: distTo[e.to()] <= distTo[e.from()] + e.weight() |
161 |
| - /// (ii) for all edge e on the SPT: distTo[e.to()] == distTo[e.from()] + e.weight() |
162 |
| - /// </summary> |
163 |
| - private bool _checkOptimalityConditions(TGraph graph, TVertex source) |
| 85 | + private void _initialize() |
164 | 86 | {
|
165 |
| - // Get the source index (to be used with the information arrays). |
166 |
| - var s = _nodesToIndices[source]; |
| 87 | + var verticesCount = _graph.VerticesCount; |
167 | 88 |
|
168 |
| - // check that edge weights are nonnegative |
169 |
| - foreach (var edge in graph.Edges) |
170 |
| - { |
171 |
| - if (edge.Weight < 0) |
172 |
| - { |
173 |
| - Console.WriteLine("Negative edge weight detected."); |
174 |
| - return false; |
175 |
| - } |
176 |
| - } |
| 89 | + _distances = new long[verticesCount]; |
| 90 | + _predecessors = new int[verticesCount]; |
177 | 91 |
|
178 |
| - // check that distTo[v] and edgeTo[v] are consistent |
179 |
| - if (_distances[s] != 0 || _predecessors[s] != NilPredecessor) |
180 |
| - { |
181 |
| - Console.WriteLine("distanceTo[s] and edgeTo[s] are inconsistent"); |
182 |
| - return false; |
183 |
| - } |
| 92 | + _nodesToIndices = new Dictionary<TVertex, int>(); |
| 93 | + _indicesToNodes = new Dictionary<int, TVertex>(); |
| 94 | + _minPriorityQueue = new MinPriorityQueue<TVertex, long>((uint)verticesCount); |
184 | 95 |
|
185 |
| - for (var v = 0; v < graph.VerticesCount; v++) |
| 96 | + var vertices = _graph.Vertices.ToList(); |
| 97 | + for (int i = 0; i < verticesCount; i++) |
186 | 98 | {
|
187 |
| - if (v == s) |
| 99 | + if (_source.Equals(vertices[i])) |
188 | 100 | {
|
189 |
| - continue; |
| 101 | + _distances[i] = 0; |
| 102 | + _predecessors[i] = 0; |
190 | 103 | }
|
191 |
| - |
192 |
| - if (_predecessors[v] == NilPredecessor && _distances[v] != Infinity) |
| 104 | + else |
193 | 105 | {
|
194 |
| - Console.WriteLine("distanceTo[] and edgeTo[] are inconsistent for at least one vertex."); |
195 |
| - return false; |
| 106 | + _distances[i] = Infinity; |
| 107 | + _predecessors[i] = NilPredecessor; |
196 | 108 | }
|
197 |
| - } |
198 | 109 |
|
199 |
| - // check that all edges e = v->w satisfy distTo[w] <= distTo[v] + e.weight() |
200 |
| - foreach (var vertex in graph.Vertices) |
201 |
| - { |
202 |
| - var v = _nodesToIndices[vertex]; |
203 |
| - |
204 |
| - foreach (var edge in graph.NeighboursMap(vertex)) |
205 |
| - { |
206 |
| - var w = _nodesToIndices[edge.Key]; |
| 110 | + _minPriorityQueue.Enqueue(vertices[i], _distances[i]); |
207 | 111 |
|
208 |
| - if (_distances[v] + edge.Value < _distances[w]) |
209 |
| - { |
210 |
| - Console.WriteLine("edge " + vertex + "-" + edge.Key + " is not relaxed"); |
211 |
| - return false; |
212 |
| - } |
213 |
| - } |
| 112 | + _nodesToIndices.Add(vertices[i], i); |
| 113 | + _indicesToNodes.Add(i, vertices[i]); |
214 | 114 | }
|
215 |
| - |
216 |
| - // check that all edges e = v->w on SPT satisfy distTo[w] == distTo[v] + e.weight() |
217 |
| - foreach (var vertex in graph.Vertices) |
218 |
| - { |
219 |
| - var w = _nodesToIndices[vertex]; |
220 |
| - |
221 |
| - if (_edgeTo[w] == null) |
222 |
| - { |
223 |
| - continue; |
224 |
| - } |
225 |
| - |
226 |
| - var edge = _edgeTo[w]; |
227 |
| - var v = _nodesToIndices[edge.Source]; |
228 |
| - |
229 |
| - if (!vertex.IsEqualTo(edge.Destination)) |
230 |
| - { |
231 |
| - return false; |
232 |
| - } |
233 |
| - |
234 |
| - if (_distances[v] + edge.Weight != _distances[w]) |
235 |
| - { |
236 |
| - Console.WriteLine("edge " + edge.Source + "-" + edge.Destination + " on shortest path not tight"); |
237 |
| - return false; |
238 |
| - } |
239 |
| - } |
240 |
| - |
241 |
| - return true; |
242 | 115 | }
|
243 | 116 |
|
244 |
| - |
245 |
| - /************************************************************************************************************/ |
246 |
| - |
247 |
| - |
248 | 117 | /// <summary>
|
249 |
| - /// Determines whether there is a path from the source vertex to this specified vertex. |
| 118 | + /// Determines whether there is a path from the source vertex to this specified vertex. |
250 | 119 | /// </summary>
|
251 | 120 | public bool HasPathTo(TVertex destination)
|
252 | 121 | {
|
253 | 122 | if (!_nodesToIndices.ContainsKey(destination))
|
254 |
| - { |
255 |
| - throw new Exception("Graph doesn't have the specified vertex."); |
256 |
| - } |
| 123 | + throw new ArgumentException("Graph doesn't have the specified vertex."); |
257 | 124 |
|
258 | 125 | var index = _nodesToIndices[destination];
|
259 | 126 | return _distances[index] != Infinity;
|
260 | 127 | }
|
261 | 128 |
|
262 | 129 | /// <summary>
|
263 |
| - /// Returns the distance between the source vertex and the specified vertex. |
| 130 | + /// Returns the distance between the source vertex and the specified vertex. |
264 | 131 | /// </summary>
|
265 | 132 | public long DistanceTo(TVertex destination)
|
266 | 133 | {
|
267 | 134 | if (!_nodesToIndices.ContainsKey(destination))
|
268 |
| - { |
269 |
| - throw new Exception("Graph doesn't have the specified vertex."); |
270 |
| - } |
| 135 | + throw new ArgumentException("Graph doesn't have the specified vertex."); |
271 | 136 |
|
272 | 137 | var index = _nodesToIndices[destination];
|
273 | 138 | return _distances[index];
|
274 | 139 | }
|
275 | 140 |
|
276 | 141 | /// <summary>
|
277 |
| - /// Returns an enumerable collection of nodes that specify the shortest path from the source vertex to the destination vertex. |
| 142 | + /// Returns an enumerable collection of nodes that specify the shortest path from the source vertex to the destination vertex. |
278 | 143 | /// </summary>
|
279 | 144 | public IEnumerable<TVertex> ShortestPathTo(TVertex destination)
|
280 | 145 | {
|
281 | 146 | if (!_nodesToIndices.ContainsKey(destination))
|
282 |
| - { |
283 |
| - throw new Exception("Graph doesn't have the specified vertex."); |
284 |
| - } |
| 147 | + throw new ArgumentException("Graph doesn't have the specified vertex."); |
285 | 148 |
|
286 | 149 | if (!HasPathTo(destination))
|
287 | 150 | {
|
288 | 151 | return null;
|
289 | 152 | }
|
290 | 153 |
|
291 | 154 | var dstIndex = _nodesToIndices[destination];
|
292 |
| - var stack = new DataStructures.Lists.Stack<TVertex>(); |
| 155 | + var stack = new Stack<TVertex>(); |
293 | 156 |
|
294 | 157 | int index;
|
295 | 158 | for (index = dstIndex; _distances[index] != 0; index = _predecessors[index])
|
296 | 159 | {
|
297 | 160 | stack.Push(_indicesToNodes[index]);
|
298 | 161 | }
|
299 |
| - |
300 |
| - // Push the source vertex |
301 | 162 | stack.Push(_indicesToNodes[index]);
|
302 | 163 |
|
303 | 164 | return stack;
|
304 | 165 | }
|
305 |
| - |
306 | 166 | }
|
307 |
| - |
308 | 167 | }
|
0 commit comments