< Summary

Information
Class: MoreStructures.Graphs.MinimumSpanningTree.PrimMstFinder
Assembly: MoreStructures
File(s): /home/runner/work/MoreStructures/MoreStructures/MoreStructures/Graphs/MinimumSpanningTree/PrimMstFinder.cs
Line coverage
100%
Covered lines: 37
Uncovered lines: 0
Coverable lines: 37
Total lines: 123
Line coverage: 100%
Branch coverage
100%
Covered branches: 14
Total branches: 14
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
get_PriorityQueueBuilder()100%1100%
.ctor(...)100%1100%
Find(...)100%14100%

File(s)

/home/runner/work/MoreStructures/MoreStructures/MoreStructures/Graphs/MinimumSpanningTree/PrimMstFinder.cs

#LineLine coverage
 1using MoreStructures.PriorityQueues;
 2using MoreStructures.PriorityQueues.BinaryHeap;
 3using MoreStructures.PriorityQueues.Extensions;
 4
 5namespace MoreStructures.Graphs.MinimumSpanningTree;
 6
 7/// <summary>
 8/// A <see cref="IMstFinder"/> implementing Prim's algorithm.
 9/// </summary>
 10/// <remarks>
 11///     <para id="algorithm">
 12///     ALGORITHM
 13///     <br/>
 14///     - The algorithm closely resembles Dijkstra's algorithm for shortest distance finding. It is a gready algorithm
 15///       selecting a vertex and the corresponding edge at each iteration, and doing that efficiently thanks to the use
 16///       of a priority queue and a dictionary of mappings from vertices to edges of shortest distance.
 17///       <br/>
 18///     - The core idea is to split the vertices of the graph into two sets: the set A of vertices already added to the
 19///       Minimum Spanning Tree MST, and the set NA of vertices which haven't been added yet to the MST.
 20///       <br/>
 21///     - At the beginning an arbitrary vertex (id = 0), is included in A and set as last added vertex LA, to bootstrap
 22///       the process. The set of edges representing the Minimum Spanning Tree, MST, is set to an empty set. The
 23///       dictionary mapping vertices to shortest distance edges BP is set to an empty dictionary. A min priority queue
 24///       of vertices, PQ, is also instantiated.
 25///       <br/>
 26///     - After that the main loop of the algorithm is executed, adding a single vertex LA to A and a single edge to
 27///       MST at each iteration, and stopping only when A contains v vertices (i.e. when MST is spanning the entire
 28///       graph).
 29///       <br/>
 30///     - At each iteration, all edges e, outgoing from and incoming into LA, and connecting LA to a vertex V not yet
 31///       in A are checked.
 32///       <br/>
 33///     - If the distance of e, d(e), is strictly smaller than the smallest known edge for V, BP[V], BP is updated and
 34///       pushed or updated in PQ.
 35///       <br/>
 36///     - After all edges of LA are checked, the vertex V' connected to A via an edge E' of shortest distance is
 37///       extracted from PQ, assigned to LA and added to A. E' is added to MST.
 38///       <br/>
 39///     - After v - 1 iterations, MST is returned as result.
 40///     </para>
 41///     <para id="complexity">
 42///     COMPLEXITY
 43///     <br/>
 44///     - All data structures initialization (A, NA, PQ, BP, MST) takes constant amount of time.
 45///       <br/>
 46///     - During the v - 1 iterations, all edges are visited at most twice: once from the source side and once from the
 47///       target side.
 48///       <br/>
 49///     - While checking and updating BP and A take a constant amount of time, pushing or updating the priority in PQ
 50///       has a runtime which depends on the specific implementation of <see cref="IUpdatablePriorityQueue{T}"/> used.
 51///       <br/>
 52///     - The analysis of the impact of the priority queue on the overall performance of the algorithm is similar to
 53///       the one done in <see cref="ShortestDistance.DijkstraShortestDistanceFinder"/>. Check that analysis for
 54///       further details.
 55///       <br/>
 56///     - In conclusion, Time Complexity is O(e + v * log(v)) with the best available implementation of an
 57///       <see cref="IUpdatablePriorityQueue{T}"/> and O((v + e) * e) with a naive implementation.
 58///       <br/>
 59///     - Space Complexity is O(v), since all data structures contains O(v) items, all of constant size.
 60///     </para>
 61/// </remarks>
 62public class PrimMstFinder : IMstFinder
 63{
 1364    private Func<IUpdatablePriorityQueue<int>> PriorityQueueBuilder { get; }
 65
 66    /// <inheritdoc cref="PrimMstFinder"/>.
 67    /// <param name="priorityQueueBuilder">
 68    /// A builder of a <see cref="IUpdatablePriorityQueue{T}"/> of <see cref="int"/> values, used by the algorithm to
 69    /// store edges with priority from the closest to any of the vertices of the MST, to the farthest.
 70    /// </param>
 1471    public PrimMstFinder(Func<IUpdatablePriorityQueue<int>> priorityQueueBuilder)
 1472    {
 1473        PriorityQueueBuilder = priorityQueueBuilder;
 1474    }
 75
 76    /// <inheritdoc path="//*[not(self::summary or self::remarks)]"/>
 77    /// <summary>
 78    /// Finds the Minimum Spanning Tree (MST) of the provided <paramref name="graph"/> using the Prim's algorithm.
 79    /// </summary>
 80    /// <remarks>
 81    ///     <inheritdoc cref="PrimMstFinder"/>
 82    /// </remarks>
 83    public ISet<(int, int)> Find(IGraph graph, IGraphDistances distances)
 1484    {
 1485        var numberOfVertices = graph.GetNumberOfVertices();
 1486        if (numberOfVertices == 0)
 187            return new HashSet<(int, int)>();
 88
 1389        var mst = new HashSet<(int, int)> { };
 90
 1391        var start = 0;
 1392        var bestPreviouses = new Dictionary<int, (int edgeDistance, int edgeStart, int edgeEnd)>();
 93
 1394        var added = new HashSet<int> { start };
 1395        var vertexes = PriorityQueueBuilder();
 1396        var lastAdded = start;
 4297        while (added.Count < numberOfVertices)
 3398        {
 25999            foreach (var (vertex, edgeStart, edgeEnd) in graph.GetAdjacentVerticesAndEdges(lastAdded, false))
 80100            {
 80101                if (added.Contains(vertex))
 33102                    continue;
 103
 47104                var newDistance = distances[(edgeStart, edgeEnd)];
 47105                if (!bestPreviouses.TryGetValue(vertex, out var bestPrevious) ||
 47106                    bestPrevious.edgeDistance > newDistance)
 41107                {
 41108                    bestPreviouses[vertex] = (newDistance, edgeStart, edgeEnd);
 41109                    vertexes.PushOrUpdate(vertex, -newDistance);
 41110                }
 47111            }
 112
 33113            if (vertexes.Count == 0)
 4114                throw new InvalidOperationException("The graph is not connected.");
 115
 29116            lastAdded = vertexes.Pop().Item;
 29117            added.Add(lastAdded);
 29118            mst.Add((bestPreviouses[lastAdded].edgeStart, bestPreviouses[lastAdded].edgeEnd));
 29119        }
 120
 9121        return mst;
 10122    }
 123}