< Summary

Information
Class: MoreStructures.Graphs.StronglyConnectedComponents.NaiveSccFinder
Assembly: MoreStructures
File(s): /home/runner/work/MoreStructures/MoreStructures/MoreStructures/Graphs/StronglyConnectedComponents/NaiveSccFinder.cs
Line coverage
100%
Covered lines: 30
Uncovered lines: 0
Coverable lines: 30
Total lines: 121
Line coverage: 100%
Branch coverage
100%
Covered branches: 6
Total branches: 6
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
get_VisitStrategy()100%1100%
.ctor(...)100%1100%
Find(...)100%6100%

File(s)

/home/runner/work/MoreStructures/MoreStructures/MoreStructures/Graphs/StronglyConnectedComponents/NaiveSccFinder.cs

#LineLine coverage
 1using MoreStructures.Graphs.Visitor;
 2
 3namespace MoreStructures.Graphs.StronglyConnectedComponents;
 4
 5/// <summary>
 6/// A simple implementation of <see cref="ISccFinder"/>, which builds a "reachability dictionary", by running a DFS
 7/// on each vertex of the graph separately.
 8/// </summary>
 9/// <remarks>
 10///     <para id="advantages">
 11///     ADVANTAGES AND DISADVANTAGES
 12///     <br/>
 13///     - This is the easiest, most straighforward implementation of <see cref="ISccFinder"/>.
 14///       <br/>
 15///     - It has, however, a cubic runtime for dense graphs, which is less than ideal.
 16///     </para>
 17///     <para id="algorithm">
 18///     ALGORITHM
 19///     <br/>
 20///     - First a dictionary R of reachable vertices is built, mapping each of the vertices of the graph to an
 21///       <see cref="HashSet{T}"/> of vertices reachable from it (including the vertex itself).
 22///       <br/>
 23///     - The dictionary is populated by running <see cref="IVisitStrategy.DepthFirstSearchFromVertex"/> on each
 24///       vertex, via the <see cref="VisitStrategy"/> provided in the constructor.
 25///       <br/>
 26///     - After that, all vertices of the graph are iterated over.
 27///       <br/>
 28///     - A SCC array, of as many items as the number of vertices in the graph, is initialized. A current SCC counter
 29///       is set to 0 and an <see cref="HashSet{T}"/> P of already processed vertices is initialized to an empty set.
 30///       <br/>
 31///     - For each vertex i, all reachable vertices j, except the ones in P, in R[i] are checked.
 32///       <br/>
 33///     - Vertices j such that R[j] contains i (including i itself, given that i always belongs to R[i]) are all
 34///       assigned the same SCC.
 35///       <br/>
 36///     - When a vertex j is assigned a SCC, it is also added to P, so that it is not checked again in following
 37///       iterations.
 38///       <br/>
 39///     - After all vertices reachable from i have been checked, the current SCC counter is incremented and the current
 40///       iteration finishes.
 41///       <br/>
 42///     - Once all vertices have been processed, the resulting SCC array is returned as result.
 43///     </para>
 44///     <para id="complexity">
 45///     COMPLEXITY
 46///     <br/>
 47///     - This algorithm creates a dictionary of reachable vertices, pretty much like
 48///       <see cref="Sorting.DfsOnEachVertexSinkBasedTopologicalSort"/>.
 49///       <br/>
 50///     - Similar considerations about the complexity of setting up the dictionary apply to this algorithm.
 51///       <br/>
 52///     - The main nested loops of the algorithm check each couple of a vertex and a reachable vertex from it.
 53///       <br/>
 54///     - The number of such couples is O(v^2), in graph where vertices are connected to O(v) other vertices.
 55///       <br/>
 56///     - Checking whether a vertex has already been processed, or whether it is reachable/reached from another vertex
 57///       are constant-time operations.
 58///       <br/>
 59///     - Therefore Time Complexity is O(v * Tdfs + v^2) and Space Complexity is O(v^2 + Sdfs), where Tdfs and Sdfs
 60///       are Time and Space Complexity of running a DFS from a vertex.
 61///       <br/>
 62///     - Time Complexity is O(v^3) in graphs where every vertex is connected to every other vertex.
 63///     </para>
 64/// </remarks>
 65public class NaiveSccFinder : ISccFinder
 66{
 67    /// <summary>
 68    /// The visitor to be used to find SCC, by running Depth First Searches on each vertex.
 69    /// </summary>
 2570    public IVisitStrategy VisitStrategy { get; }
 71
 72    /// <summary>
 73    ///     <inheritdoc cref="NaiveSccFinder"/>
 74    /// </summary>
 75    /// <param name="visitStrategy">
 76    ///     <inheritdoc cref="VisitStrategy" path="/summary"/>
 77    /// </param>
 78    /// <remarks>
 79    ///     <inheritdoc cref="NaiveSccFinder"/>
 80    /// </remarks>
 1081    public NaiveSccFinder(IVisitStrategy visitStrategy)
 1082    {
 1083        VisitStrategy = visitStrategy;
 1084    }
 85
 86    /// <inheritdoc path="//*[not(self::remarks)]"/>
 87    /// <remarks>
 88    ///     <inheritdoc cref="NaiveSccFinder" path="/remarks"/>
 89    /// </remarks>
 90    public IList<int> Find(IGraph graph)
 1091    {
 1092        var numberOfVertices = graph.GetNumberOfVertices();
 1093        var verticesReachableFromVertices = Enumerable
 1094            .Range(0, numberOfVertices)
 2595            .Select(vertex => VisitStrategy.DepthFirstSearchFromVertex(graph, vertex).ToHashSet())
 1096            .ToList();
 97
 1098        var scc = new int[numberOfVertices];
 1099        var currentScc = 0;
 10100        var processedVertices = new HashSet<int>();
 70101        for (var i = 0; i < numberOfVertices; i++)
 25102        {
 25103            if (processedVertices.Contains(i))
 8104                continue;
 105
 17106            var verticesSameScc = verticesReachableFromVertices[i]
 17107                .Except(processedVertices)
 46108                .Where(j => verticesReachableFromVertices[j].Contains(i));
 109
 101110            foreach (var j in verticesSameScc)
 25111            {
 25112                scc[j] = currentScc;
 25113                processedVertices.Add(j);
 25114            }
 115
 17116            currentScc++;
 17117        }
 118
 10119        return scc;
 10120    }
 121}