Skip to content

Commit

Permalink
Fix Dijkstra algorithm (#499)
Browse files Browse the repository at this point in the history
  • Loading branch information
wm55414 authored Nov 27, 2024
1 parent 0184c37 commit 0ef9849
Show file tree
Hide file tree
Showing 2 changed files with 151 additions and 50 deletions.
134 changes: 130 additions & 4 deletions Algorithms.Tests/Graph/Dijkstra/DijkstraTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,136 @@ public void DijkstraTest4_Success()
shortestPathList[2].ToString().Should()
.Be($"Vertex: {c} - Distance: {3} - Previous: {a}");

// Vertex D won't be visited in this dijkstra implementation which is valid only for cyclic graphs,
// since it is necessary to backtrack all unvisited vertices and place them
// to the priority queue, which is not implemented yet in this repository.
// If algo goes to the next vertex with minimal distance and this vertex is leaf -- algorithm stops.
shortestPathList[3].Vertex.Should().Be(d);
shortestPathList[3].Distance.Should().Be(8);
shortestPathList[3].PreviousVertex.Should().Be(c);
shortestPathList[3].ToString().Should()
.Be($"Vertex: {d} - Distance: {8} - Previous: {c}");
}

[Test]
public void DijkstraTest5_Success()
{
// here test case is from https://www.youtube.com/watch?v=pVfj6mxhdMw

var graph = new DirectedWeightedGraph<char>(7);
var a = graph.AddVertex('A');
var b = graph.AddVertex('B');
var c = graph.AddVertex('C');
var d = graph.AddVertex('D');
var e = graph.AddVertex('E');
var w = graph.AddVertex('W');
var z = graph.AddVertex('Z');

graph.AddEdge(a, b, 6);
graph.AddEdge(b, a, 6);

graph.AddEdge(a, d, 1);
graph.AddEdge(d, a, 1);

graph.AddEdge(d, e, 1);
graph.AddEdge(e, d, 1);

graph.AddEdge(d, b, 2);
graph.AddEdge(b, d, 2);

graph.AddEdge(e, b, 2);
graph.AddEdge(b, e, 2);

graph.AddEdge(e, c, 5);
graph.AddEdge(c, e, 5);

graph.AddEdge(c, b, 5);
graph.AddEdge(b, c, 5);

graph.AddEdge(a, w, 50);
graph.AddEdge(w, a, 50);

graph.AddEdge(w, z, 1);
graph.AddEdge(z, w, 1);

var shortestPathList = DijkstraAlgorithm.GenerateShortestPath(graph, a);
shortestPathList.Length.Should().Be(7);

shortestPathList[0].Vertex.Should().Be(a);
shortestPathList[0].Distance.Should().Be(0);
shortestPathList[0].PreviousVertex.Should().Be(a);
shortestPathList[0].ToString().Should()
.Be($"Vertex: {a} - Distance: {0} - Previous: {a}");

shortestPathList[1].Vertex.Should().Be(b);
shortestPathList[1].Distance.Should().Be(3);
shortestPathList[1].PreviousVertex.Should().Be(d);
shortestPathList[1].ToString().Should()
.Be($"Vertex: {b} - Distance: {3} - Previous: {d}");

shortestPathList[2].Vertex.Should().Be(c);
shortestPathList[2].Distance.Should().Be(7);
shortestPathList[2].PreviousVertex.Should().Be(e);
shortestPathList[2].ToString().Should()
.Be($"Vertex: {c} - Distance: {7} - Previous: {e}");

shortestPathList[3].Vertex.Should().Be(d);
shortestPathList[3].Distance.Should().Be(1);
shortestPathList[3].PreviousVertex.Should().Be(a);
shortestPathList[3].ToString().Should()
.Be($"Vertex: {d} - Distance: {1} - Previous: {a}");

shortestPathList[4].Vertex.Should().Be(e);
shortestPathList[4].Distance.Should().Be(2);
shortestPathList[4].PreviousVertex.Should().Be(d);
shortestPathList[4].ToString().Should()
.Be($"Vertex: {e} - Distance: {2} - Previous: {d}");

shortestPathList[5].Vertex.Should().Be(w);
shortestPathList[5].Distance.Should().Be(50);
shortestPathList[5].PreviousVertex.Should().Be(a);
shortestPathList[5].ToString().Should()
.Be($"Vertex: {w} - Distance: {50} - Previous: {a}");

shortestPathList[6].Vertex.Should().Be(z);
shortestPathList[6].Distance.Should().Be(51);
shortestPathList[6].PreviousVertex.Should().Be(w);
shortestPathList[6].ToString().Should()
.Be($"Vertex: {z} - Distance: {51} - Previous: {w}");
}

[Test]
public void DijkstraTest6_Success()
{
var graph = new DirectedWeightedGraph<char>(5);
var a = graph.AddVertex('A');
var b = graph.AddVertex('B');
var c = graph.AddVertex('C');
var d = graph.AddVertex('D');

graph.AddEdge(a, b, 1);
graph.AddEdge(b, a, 1);

graph.AddEdge(c, d, 5);
graph.AddEdge(d, c, 5);

var shortestPathList = DijkstraAlgorithm.GenerateShortestPath(graph, a);

shortestPathList.Length.Should().Be(4);
shortestPathList[0].Vertex.Should().Be(a);
shortestPathList[0].Distance.Should().Be(0);
shortestPathList[0].PreviousVertex.Should().Be(a);
shortestPathList[0].ToString().Should()
.Be($"Vertex: {a} - Distance: {0} - Previous: {a}");

shortestPathList[1].Vertex.Should().Be(b);
shortestPathList[1].Distance.Should().Be(1);
shortestPathList[1].PreviousVertex.Should().Be(a);
shortestPathList[1].ToString().Should()
.Be($"Vertex: {b} - Distance: {1} - Previous: {a}");

shortestPathList[2].Vertex.Should().Be(c);
shortestPathList[2].Distance.Should().Be(double.MaxValue);
shortestPathList[2].PreviousVertex.Should().BeNull();
shortestPathList[2].ToString().Should()
.Be($"Vertex: {c} - Distance: {double.MaxValue} - Previous: {null}");

shortestPathList[3].Vertex.Should().Be(d);
shortestPathList[3].Distance.Should().Be(double.MaxValue);
shortestPathList[3].PreviousVertex.Should().BeNull();
Expand Down
67 changes: 21 additions & 46 deletions Algorithms/Graph/Dijkstra/DijkstraAlgorithm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,44 +25,43 @@ public static DistanceModel<T>[] GenerateShortestPath<T>(DirectedWeightedGraph<T

var distanceArray = InitializeDistanceArray(graph, startVertex);

var currentVertex = startVertex;
var distanceRecord = new PriorityQueue<DistanceModel<T>, double>();

var currentPath = 0d;
distanceRecord.Enqueue(distanceArray[0], distanceArray[0].Distance);

while (true)
while (visitedVertices.Count != distanceArray.Length && distanceRecord.Count != 0)
{
visitedVertices.Add(currentVertex);
while(visitedVertices.Contains(distanceRecord.Peek().Vertex!))
{
distanceRecord.Dequeue();
}

var minDistance = distanceRecord.Dequeue();

var currentPath = minDistance.Distance;

visitedVertices.Add(minDistance.Vertex!);

var neighborVertices = graph
.GetNeighbors(currentVertex)
.GetNeighbors(minDistance.Vertex!)
.Where(x => x != null && !visitedVertices.Contains(x))
.ToList();

foreach (var vertex in neighborVertices)
{
var adjacentDistance = graph.AdjacentDistance(currentVertex, vertex!);
var adjacentDistance = graph.AdjacentDistance(minDistance.Vertex!, vertex!);

var distance = distanceArray[vertex!.Index];

if (distance.Distance <= currentPath + adjacentDistance)
var fullDistance = currentPath + adjacentDistance;

if (distance.Distance > fullDistance)
{
continue;
distance.Distance = fullDistance;
distance.PreviousVertex = minDistance.Vertex;
distanceRecord.Enqueue(distance, fullDistance);
}

distance.Distance = currentPath + adjacentDistance;
distance.PreviousVertex = currentVertex;
}

var minimalAdjacentVertex = GetMinimalUnvisitedAdjacentVertex(graph, currentVertex, neighborVertices);

if (neighborVertices.Count == 0 || minimalAdjacentVertex is null)
{
break;
}

currentPath += graph.AdjacentDistance(currentVertex, minimalAdjacentVertex);

currentVertex = minimalAdjacentVertex;
}

return distanceArray;
Expand Down Expand Up @@ -96,28 +95,4 @@ private static void ValidateGraphAndStartVertex<T>(DirectedWeightedGraph<T> grap
throw new ArgumentNullException(nameof(graph));
}
}

private static Vertex<T>? GetMinimalUnvisitedAdjacentVertex<T>(
IDirectedWeightedGraph<T> graph,
Vertex<T> startVertex,
IEnumerable<Vertex<T>?> adjacentVertices)
{
var minDistance = double.MaxValue;
Vertex<T>? minVertex = default;

foreach (var vertex in adjacentVertices)
{
var currentDistance = graph.AdjacentDistance(startVertex, vertex!);

if (minDistance <= currentDistance)
{
continue;
}

minDistance = currentDistance;
minVertex = vertex;
}

return minVertex;
}
}

0 comments on commit 0ef9849

Please sign in to comment.