| | 1 | | namespace MoreStructures.Graphs; |
| | 2 | |
|
| | 3 | | /// <summary> |
| | 4 | | /// A graph data structure, represented as an unsorted list of unlabelled edges, connecting unlabelled vertices. |
| | 5 | | /// </summary> |
| | 6 | | /// <param name="NumberOfVertices"> |
| | 7 | | /// The total number n of vertices in the graph, identified with ids from 0 to n - 1. |
| | 8 | | /// </param> |
| | 9 | | /// <param name="Edges"> |
| | 10 | | /// The list of edges of the graph, each one represented as a couple of ids of the vertices which constitute the |
| | 11 | | /// extremes of the edge. Edges can be considered as directional or not, depending on the scenario. |
| | 12 | | /// </param> |
| | 13 | | /// <remarks> |
| | 14 | | /// - <b>This representation does support multigraphs</b>, i.e. graphs which can have multiple parallel edges |
| | 15 | | /// between the same two vertices. |
| | 16 | | /// <br/> |
| | 17 | | /// - If the edges are considered directional, i.e. (s, e) is considered as a different edge from (e, s), the resulting |
| | 18 | | /// graph is directed. Otherwise, the resulting graph is undirected. |
| | 19 | | /// <br/> |
| | 20 | | /// - The size of this data structure is proportional to the number of edges of the graph. |
| | 21 | | /// <br/> |
| | 22 | | /// - So, this graph representation is particularly useful when the number is edges is smaller or proportional to the |
| | 23 | | /// number of vertices in the graph, i.e. when the graph is <b>sparse</b> (i.e. when e is O(v)). |
| | 24 | | /// <br/> |
| | 25 | | /// - It becomes an expensive representation when the graph is <b>dense</b> (i.e. when e is O(v^2)). |
| | 26 | | /// <br/> |
| | 27 | | /// - While having size proportional to the number of edges, <see cref="EdgeListGraph"/> is less convenient than |
| | 28 | | /// <see cref="AdjacencyListGraph"/> to run neighborhood-based algorithms, such as discovery, because it makes more |
| | 29 | | /// complex and slower to get neighbors of a vertex. |
| | 30 | | /// </remarks> |
| | 31 | | /// <example> |
| | 32 | | /// The followin graph: |
| | 33 | | /// <code> |
| | 34 | | /// 0 --> 1 <==> 3 |
| | 35 | | /// | ^ ^ / |
| | 36 | | /// | | / / |
| | 37 | | /// | | / / |
| | 38 | | /// v |/ / |
| | 39 | | /// 2 <----- |
| | 40 | | /// </code> |
| | 41 | | /// is represented as <c>EdgeListGraph(4, new { (0, 1), (0, 2), (1, 3), (2, 0), (2, 1), (3, 1), (3, 2) })</c>. |
| | 42 | | /// </example> |
| 45568 | 43 | | public record EdgeListGraph(int NumberOfVertices, IList<(int start, int end)> Edges) : IGraph |
| 734 | 44 | | { |
| 734 | 45 | | /// <inheritdoc path="//*[not(self::remarks)]" /> |
| 734 | 46 | | /// <remarks> |
| 734 | 47 | | /// In the <see cref="EdgeListGraph"/> representation, it's explicitely set in <see cref="NumberOfVertices"/>. |
| 734 | 48 | | /// <br/> |
| 734 | 49 | | /// Time and Space Complexity are O(1). |
| 734 | 50 | | /// </remarks> |
| 2918 | 51 | | public int GetNumberOfVertices() => NumberOfVertices; |
| 734 | 52 | |
|
| 734 | 53 | | /// <inheritdoc path="//*[not(self::remarks)]" /> |
| 734 | 54 | | /// <remarks> |
| 734 | 55 | | /// <para id="algorithm"> |
| 734 | 56 | | /// ALGORITHM |
| 734 | 57 | | /// <br/> |
| 734 | 58 | | /// - Just returns the underlying list of edges, as is. |
| 734 | 59 | | /// </para> |
| 734 | 60 | | /// <para id="complexity"> |
| 734 | 61 | | /// COMPLEXITY |
| 734 | 62 | | /// <br/> |
| 734 | 63 | | /// - Time and Space Complexity are O(1). |
| 734 | 64 | | /// </para> |
| 734 | 65 | | /// </remarks> |
| 734 | 66 | |
|
| 54 | 67 | | public IEnumerable<(int edgeStart, int edgeEnd)> GetAllEdges() => Edges; |
| 734 | 68 | |
|
| 734 | 69 | |
|
| 734 | 70 | | /// <inheritdoc path="//*[not(self::remarks)]" /> |
| 734 | 71 | | /// <remarks> |
| 734 | 72 | | /// <para id="algorithm"> |
| 734 | 73 | | /// ALGORITHM |
| 734 | 74 | | /// <br/> |
| 734 | 75 | | /// - Because the edge list is unsorted, and there is no "index" or additional data structure which can help |
| 734 | 76 | | /// retrieving neighbors quickly, the algorithm has to linearly scan the entire edge list, looking for |
| 734 | 77 | | /// neighbors. |
| 734 | 78 | | /// </para> |
| 734 | 79 | | /// <para id="complexity"> |
| 734 | 80 | | /// COMPLEXITY |
| 734 | 81 | | /// <br/> |
| 734 | 82 | | /// - Checking whether each edge is a neighbor or not only requires comparing the edge start, when taking into |
| 734 | 83 | | /// account edge directions, or both start and end, when considering edges as undirected. |
| 734 | 84 | | /// <br/> |
| 734 | 85 | | /// - Both are constant time operations. |
| 734 | 86 | | /// <br/> |
| 734 | 87 | | /// - Therefore Time Complexity is O(e), where e is the number of edges, and Space Complexity is O(1). |
| 734 | 88 | | /// </para> |
| 734 | 89 | | /// </remarks> |
| 734 | 90 | | public IEnumerable<IGraph.Adjacency> GetAdjacentVerticesAndEdges( |
| 734 | 91 | | int start, bool takeIntoAccountEdgeDirection) |
| 39445 | 92 | | { |
| 39445 | 93 | | if (takeIntoAccountEdgeDirection) |
| 38592 | 94 | | { |
| 38592 | 95 | | var adjacencies = Edges |
| 490496 | 96 | | .Where(edge => edge.start == start) |
| 86651 | 97 | | .Select(edge => new IGraph.Adjacency(edge.end, edge.start, edge.end)); |
| 211812 | 98 | | foreach (var adjacency in adjacencies) |
| 48059 | 99 | | yield return adjacency; |
| 38510 | 100 | | yield break; |
| 734 | 101 | | } |
| 734 | 102 | |
|
| 9851 | 103 | | foreach (var edge in Edges) |
| 3646 | 104 | | { |
| 3646 | 105 | | if (edge.start == start) |
| 871 | 106 | | { |
| 871 | 107 | | yield return new(edge.end, edge.start, edge.end); |
| 871 | 108 | | continue; |
| 734 | 109 | | } |
| 2775 | 110 | | if (edge.end == start) |
| 765 | 111 | | { |
| 765 | 112 | | yield return new(edge.start, edge.start, edge.end); |
| 765 | 113 | | } |
| 2775 | 114 | | } |
| 853 | 115 | | } |
| 734 | 116 | |
|
| 734 | 117 | | /// <inheritdoc path="//*[not(self::remarks)]" /> |
| 734 | 118 | | /// <remarks> |
| 734 | 119 | | /// <para id="algorithm"> |
| 734 | 120 | | /// ALGORITHM |
| 734 | 121 | | /// <br/> |
| 734 | 122 | | /// - An <see cref="IGraph"/> proxy is created, wrapping this instance of <see cref="IGraph"/>. |
| 734 | 123 | | /// <br/> |
| 734 | 124 | | /// - <see cref="IGraph.GetNumberOfVertices"/> is dispatched to the proxied graph. |
| 734 | 125 | | /// <br/> |
| 734 | 126 | | /// - <see cref="IGraph.GetAdjacentVerticesAndEdges(int, bool)"/> is directly implemented, accessing |
| 734 | 127 | | /// <see cref="Edges"/> directly. |
| 734 | 128 | | /// <br/> |
| 734 | 129 | | /// - The implementation is very similar to the one of <see cref="EdgeListGraph"/>: the only difference |
| 734 | 130 | | /// is that start and end vertices of each edge are inverted. |
| 734 | 131 | | /// </para> |
| 734 | 132 | | /// <para id="complexity"> |
| 734 | 133 | | /// COMPLEXITY |
| 734 | 134 | | /// <br/> |
| 734 | 135 | | /// - Since this method just creates a proxy, Time and Space Complexity are O(1). |
| 734 | 136 | | /// <br/> |
| 734 | 137 | | /// - All operations on the proxy have the same Time and Space Complexity as the corresponding methods in |
| 734 | 138 | | /// <see cref="EdgeListGraph"/>. |
| 734 | 139 | | /// </para> |
| 734 | 140 | | /// </remarks> |
| 574 | 141 | | public IGraph Reverse() => new ReverseGraph(this); |
| 734 | 142 | |
|
| 734 | 143 | | sealed private class ReverseGraph : ReverseProxyGraph<EdgeListGraph> |
| 734 | 144 | | { |
| 574 | 145 | | public ReverseGraph(EdgeListGraph graph) : base(graph) |
| 574 | 146 | | { |
| 574 | 147 | | } |
| 734 | 148 | |
|
| 734 | 149 | | public override IEnumerable<IGraph.Adjacency> GetAdjacentVerticesAndEdges( |
| 734 | 150 | | int start, bool takeIntoAccountEdgeDirection) |
| 949 | 151 | | { |
| 949 | 152 | | var edges = Proxied.Edges; |
| 734 | 153 | |
|
| 949 | 154 | | if (takeIntoAccountEdgeDirection) |
| 923 | 155 | | { |
| 923 | 156 | | var adjacencies = edges |
| 7154 | 157 | | .Where(edge => edge.end == start) |
| 1811 | 158 | | .Select(edge => new IGraph.Adjacency(edge.start, edge.end, edge.start)); |
| 4543 | 159 | | foreach (var adjacency in adjacencies) |
| 888 | 160 | | yield return adjacency; |
| 921 | 161 | | yield break; |
| 734 | 162 | | } |
| 734 | 163 | |
|
| 346 | 164 | | foreach (var edge in edges) |
| 134 | 165 | | { |
| 134 | 166 | | if (edge.end == start) |
| 23 | 167 | | { |
| 23 | 168 | | yield return new(edge.start, edge.end, edge.start); |
| 23 | 169 | | } |
| 734 | 170 | |
|
| 134 | 171 | | if (edge.start == start) |
| 23 | 172 | | { |
| 23 | 173 | | yield return new(edge.end, edge.end, edge.start); |
| 23 | 174 | | continue; |
| 734 | 175 | | } |
| 111 | 176 | | } |
| 26 | 177 | | } |
| 734 | 178 | | } |
| 734 | 179 | | } |