Skip to content

Commit f22481f

Browse files
authored
Merge pull request #116 from PatrykOlejniczak/fix-dijkstra-shortest-paths
Fix Dijkstra shortest path algorithm + tests.
2 parents fa0cd74 + c88890b commit f22481f

File tree

2 files changed

+299
-330
lines changed

2 files changed

+299
-330
lines changed
Lines changed: 62 additions & 203 deletions
Original file line numberDiff line numberDiff line change
@@ -1,308 +1,167 @@
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;
82
using DataStructures.Heaps;
93
using System;
104
using System.Collections.Generic;
5+
using System.Linq;
116

127
namespace Algorithms.Graphs
138
{
9+
/// <summary>
10+
/// Computes Dijkstra's Shortest-Paths for Directed Weighted Graphs from a single-source to all destinations.
11+
/// </summary>
1412
public class DijkstraShortestPaths<TGraph, TVertex>
1513
where TGraph : IGraph<TVertex>, IWeightedGraph<TVertex>
1614
where TVertex : IComparable<TVertex>
1715
{
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+
2319
private long[] _distances;
2420
private int[] _predecessors;
25-
private WeightedEdge<TVertex>[] _edgeTo;
2621

27-
// A dictionary that maps node-values to integer indeces
2822
private Dictionary<TVertex, int> _nodesToIndices;
29-
30-
// A dictionary that maps integer index to node-value
3123
private Dictionary<int, TVertex> _indicesToNodes;
3224

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;
3626

27+
private readonly TGraph _graph;
28+
private readonly TVertex _source;
3729

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)
4331
{
44-
if (Graph == null)
45-
{
46-
throw new ArgumentNullException();
47-
}
32+
if (graph == null)
33+
throw new ArgumentNullException(nameof(graph));
4834

49-
if (!Graph.HasVertex(Source))
50-
{
35+
if (source == null)
36+
throw new ArgumentNullException(nameof(source));
37+
38+
if (!graph.HasVertex(source))
5139
throw new ArgumentException("The source vertex doesn't belong to graph.");
52-
}
5340

54-
// Init
55-
_initializeDataMembers(Graph);
41+
if (graph.Edges.Any(edge => edge.Weight < 0))
42+
throw new ArgumentException("Negative edge weight detected.");
5643

57-
// Traverse the graph
58-
_dijkstra(Graph, Source);
44+
_graph = graph;
45+
_source = source;
5946

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();
6549
}
6650

67-
68-
/************************************************************************************************************/
69-
70-
7151
/// <summary>
72-
/// The Dijkstra's algorithm.
52+
/// The Dijkstra's algorithm.
7353
/// </summary>
74-
private void _dijkstra(TGraph graph, TVertex source)
54+
private void _dijkstra()
7555
{
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)
8557
{
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];
8960

90-
foreach (var edge in edges)
61+
var outgoingEdges = _graph.OutgoingEdges(currentVertex);
62+
foreach (var outgoingEdge in outgoingEdges)
9163
{
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;
10066

101-
// Relax the edge
102-
// if check is true, a shorter path is found from current to adjacent
10367
if (delta < _distances[adjacentIndex])
10468
{
105-
_edgeTo[adjacentIndex] = edge;
10669
_distances[adjacentIndex] = delta;
107-
_predecessors[adjacentIndex] = currentIndex;
70+
_predecessors[adjacentIndex] = currentVertexIndex;
10871

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))
11173
{
112-
minPQ.UpdatePriority(edge.Destination, delta);
74+
_minPriorityQueue.UpdatePriority(outgoingEdge.Destination, delta);
11375
}
11476
else
11577
{
116-
minPQ.Enqueue(edge.Destination, delta);
78+
_minPriorityQueue.Enqueue(outgoingEdge.Destination, delta);
11779
}
11880
}
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;
14581
}
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;
15582
}
15683
}
15784

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()
16486
{
165-
// Get the source index (to be used with the information arrays).
166-
var s = _nodesToIndices[source];
87+
var verticesCount = _graph.VerticesCount;
16788

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];
17791

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);
18495

185-
for (var v = 0; v < graph.VerticesCount; v++)
96+
var vertices = _graph.Vertices.ToList();
97+
for (int i = 0; i < verticesCount; i++)
18698
{
187-
if (v == s)
99+
if (_source.Equals(vertices[i]))
188100
{
189-
continue;
101+
_distances[i] = 0;
102+
_predecessors[i] = 0;
190103
}
191-
192-
if (_predecessors[v] == NilPredecessor && _distances[v] != Infinity)
104+
else
193105
{
194-
Console.WriteLine("distanceTo[] and edgeTo[] are inconsistent for at least one vertex.");
195-
return false;
106+
_distances[i] = Infinity;
107+
_predecessors[i] = NilPredecessor;
196108
}
197-
}
198109

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]);
207111

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]);
214114
}
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;
242115
}
243116

244-
245-
/************************************************************************************************************/
246-
247-
248117
/// <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.
250119
/// </summary>
251120
public bool HasPathTo(TVertex destination)
252121
{
253122
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.");
257124

258125
var index = _nodesToIndices[destination];
259126
return _distances[index] != Infinity;
260127
}
261128

262129
/// <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.
264131
/// </summary>
265132
public long DistanceTo(TVertex destination)
266133
{
267134
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.");
271136

272137
var index = _nodesToIndices[destination];
273138
return _distances[index];
274139
}
275140

276141
/// <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.
278143
/// </summary>
279144
public IEnumerable<TVertex> ShortestPathTo(TVertex destination)
280145
{
281146
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.");
285148

286149
if (!HasPathTo(destination))
287150
{
288151
return null;
289152
}
290153

291154
var dstIndex = _nodesToIndices[destination];
292-
var stack = new DataStructures.Lists.Stack<TVertex>();
155+
var stack = new Stack<TVertex>();
293156

294157
int index;
295158
for (index = dstIndex; _distances[index] != 0; index = _predecessors[index])
296159
{
297160
stack.Push(_indicesToNodes[index]);
298161
}
299-
300-
// Push the source vertex
301162
stack.Push(_indicesToNodes[index]);
302163

303164
return stack;
304165
}
305-
306166
}
307-
308167
}

0 commit comments

Comments
 (0)