diff --git a/Algorithms.Tests/Graph/Dijkstra/DijkstraTests.cs b/Algorithms.Tests/Graph/Dijkstra/DijkstraTests.cs index 5f450e08..e61778bb 100644 --- a/Algorithms.Tests/Graph/Dijkstra/DijkstraTests.cs +++ b/Algorithms.Tests/Graph/Dijkstra/DijkstraTests.cs @@ -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(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(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(); diff --git a/Algorithms/Graph/Dijkstra/DijkstraAlgorithm.cs b/Algorithms/Graph/Dijkstra/DijkstraAlgorithm.cs index 113b1ece..8aee1f0f 100644 --- a/Algorithms/Graph/Dijkstra/DijkstraAlgorithm.cs +++ b/Algorithms/Graph/Dijkstra/DijkstraAlgorithm.cs @@ -25,44 +25,43 @@ public static DistanceModel[] GenerateShortestPath(DirectedWeightedGraph, 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; @@ -96,28 +95,4 @@ private static void ValidateGraphAndStartVertex(DirectedWeightedGraph grap throw new ArgumentNullException(nameof(graph)); } } - - private static Vertex? GetMinimalUnvisitedAdjacentVertex( - IDirectedWeightedGraph graph, - Vertex startVertex, - IEnumerable?> adjacentVertices) - { - var minDistance = double.MaxValue; - Vertex? minVertex = default; - - foreach (var vertex in adjacentVertices) - { - var currentDistance = graph.AdjacentDistance(startVertex, vertex!); - - if (minDistance <= currentDistance) - { - continue; - } - - minDistance = currentDistance; - minVertex = vertex; - } - - return minVertex; - } }