/// <summary> /// Computes the minimum spanning tree for the connected component containing a specified vertex. /// </summary> /// <param name="startVertex">The vertex where the algorithm should start.</param> /// <returns>The set of edges that define the minimum spanning tree.</returns> public ReadOnlySpan <TEdgeId> ComputeMinimumSpanningTree(TVertexId startVertex) { var heapNodes = new Dictionary <TVertexId, PairingHeap <VertexWithDistanceAndEdge> .ElementPointer>(this._graph.Comparer) { { startVertex, PairingHeap <VertexWithDistanceAndEdge> .ElementPointer.Undefined } }; var todo = new PairingHeap <VertexWithDistanceAndEdge>(new DistanceComparer(this._comparer)); var result = new DynamicArray <TEdgeId>(true); ProcessEdges(startVertex); while (todo.TryExtractMinimum(out var next)) { heapNodes[next.Vertex] = PairingHeap <VertexWithDistanceAndEdge> .ElementPointer.Undefined; result.AddLast(next.Edge); ProcessEdges(next.Vertex); } return(result.AsSpan()); void ProcessEdges(TVertexId vertex) { foreach (var outEdgeIdx in this._graph.GetOutEdges(vertex)) { var target = this._graph.GetTarget(outEdgeIdx); if (heapNodes.TryGetValue(target, out var vertexState)) { if (vertexState.IsUndefined) { continue; } var currentBestDistanceToTarget = todo[vertexState].Distance; var distance = this._graph.GetEdgeTag(outEdgeIdx); if (this._comparer.Compare(distance, currentBestDistanceToTarget) >= 0) { continue; } todo.Decrease(vertexState, new(target, distance, outEdgeIdx)); } else { var distance = this._graph.GetEdgeTag(outEdgeIdx); var node = todo.Insert(new(target, distance, outEdgeIdx)); heapNodes.Add(target, node); } } } }
/// <summary> /// Computes the shortest path between two specified vertices. /// </summary> /// <param name="startVertex">The vertex where the path should start.</param> /// <param name="targetVertex">The vertex where the path should end.</param> /// <param name="distance">The shortest distance from <paramref name="startVertex"/> to <paramref name="targetVertex"/>.</param> /// <returns>The shortest path, represented as a span of edges.</returns> /// <exception cref="ArgumentException">There is no path from <paramref name="startVertex"/> to <paramref name="targetVertex"/>.</exception> public ReadOnlySpan <TEdgeId> ComputeShortestPath(TVertexId startVertex, TVertexId targetVertex, out TDistance distance) { var vertices = new Dictionary <TVertexId, VertexInfo>(this._graph.Comparer); var queue = new PairingHeap <TVertexId, TDistance>(this._calculator); var startHeapElement = queue.Insert(new(startVertex, this._calculator.Zero)); var startVertexInfo = new VertexInfo(this._heuristicDistanceCalculator(startVertex, targetVertex)) { DistanceToVertex = this._calculator.Zero, HeapElement = startHeapElement }; vertices.Add(startVertex, startVertexInfo); while (queue.TryExtractMinimum(out var next)) { var currentVertexInfo = vertices[next.Key]; var distanceSoFar = currentVertexInfo.DistanceToVertex; currentVertexInfo.HeapElement = PairingHeap <KeyValuePair <TVertexId, TDistance> > .ElementPointer.Undefined; if (this._graph.Equals(next.Key, targetVertex)) { distance = distanceSoFar; return(this.ResolveShortestPath(startVertex, targetVertex, vertices)); } var edges = this._graph.GetOutEdges(next.Key); foreach (var edgeId in edges) { var target = this._graph.GetTarget(edgeId); var currentDistance = this._calculator.Add(distanceSoFar, this._graph.GetEdgeTag(edgeId)); if (vertices.TryGetValue(target, out var vertexInfo)) { if (this._calculator.Compare(currentDistance, vertexInfo.DistanceToVertex) >= 0) { continue; } vertexInfo.DistanceToVertex = currentDistance; vertexInfo.ShortestPathSource = next.Key; vertexInfo.ShortestPathEdge = edgeId; var priority = this._calculator.Add(currentDistance, vertexInfo.HeuristicDistanceToTarget); if (vertexInfo.HeapElement.IsUndefined) { vertexInfo.HeapElement = queue.Insert(target, priority); } else { queue.Decrease(vertexInfo.HeapElement, priority); } } else { var heuristicDistance = this._heuristicDistanceCalculator(target, targetVertex); var priority = this._calculator.Add(currentDistance, heuristicDistance); var heapElement = queue.Insert(target, priority); vertexInfo = new(heuristicDistance) { HeapElement = heapElement, DistanceToVertex = currentDistance, ShortestPathSource = next.Key, ShortestPathEdge = edgeId }; vertices.Add(target, vertexInfo); } } } throw new ArgumentException("Target vertex not reachable from start vertex.", nameof(targetVertex)); }