/// <summary> /// Single-source multiple-target version of Dijkstra's shortest path algorithm that returns shortest paths to a specified number of targets. /// </summary> /// <param name="source">Source vertex to start searching from.</param> /// <param name="targets">Target vertices to find shortest paths to.</param> /// <param name="count">Number of targets to find.</param> /// <returns>Mapping of all shortest paths from the source to all vertices up to <paramref name="count"/> targets.</returns> /// <remarks> /// The graph attached to <paramref name="source"/> should not contain any negative cycles. /// </remarks> public static IDictionary<IVertex, Path> ShortestPaths(IVertex source, ICollection<IVertex> targets, int count) { if (source == null) throw new ArgumentNullException("source"); if (targets == null) throw new ArgumentNullException("targets"); if (count < 1) throw new ArgumentOutOfRangeException("count"); // Create a priority queue where target vertices are prioritized by shortest path weight, starting with the source. PriorityQueue<IVertex, int> queue = new PriorityQueue<IVertex, int>(); IDictionary<IVertex, IEdge/*?*/> edges = new Dictionary<IVertex, IEdge/*?*/>(); // Initialize the source with a path of 0 length. queue[source] = 0; edges[source] = null; // Keep track of the explored vertices with known minimal length. IDictionary<IVertex, Path> shortestPaths = new Dictionary<IVertex, Path>(); // The number of explored targets. int targetsFound = 0; // Keep track of the upper bound on the distance to the closest 'count' nodes. // See "A Heuristic for Dijkstra's Algorithm" for details. PriorityQueue<IVertex, int> bounds = new PriorityQueue<IVertex, int>(true); foreach (IVertex target in targets) { bounds.Enqueue(target, int.MaxValue); if (bounds.Count == count) break; } // Traverse the queue. while (queue.Count > 0) { // Get the next closest vertex and copy it to the list of shortest paths. KeyValuePair<IVertex, int> item = queue.Dequeue(); source = item.Key; // We've found the shortest path to this vertex. IEdge/*?*/ shortest = edges[source]; if (shortest == null) shortestPaths[source] = new Path(source); else shortestPaths[source] = shortestPaths[shortest.Source] + shortest; // If we've explored the requested number of targets then break early. if (targets.Contains(source)) if (++targetsFound >= count) break; // Relax all outgoing edges from this vertex. foreach (IEdge edge in source.Edges) { // If we haven't explored the target yet. IVertex target = edge.Target; if (!shortestPaths.ContainsKey(target)) { // Calculate the new cost via this edge. int newCost = item.Value + edge.Cost; // Only relax the edge if the cost is an improvement (i.e. less than) the shortest paths seen so far. // See "A Heuristic for Dijkstra's Algorithm" for details. if (newCost < bounds.Peek().Value) { // Compare the new path with the current paths. int currentCost; if (!queue.TryGetValue(target, out currentCost) || newCost < currentCost) { // If no current path exists or the new path is shorter, then update the shortest path in the queue. queue[target] = newCost; edges[target] = edge; // If the target is a final target then update the cost in the list of upper bounds. if (targets.Contains(target)) { bounds[target] = newCost; // We only need to keep track of the closest 'count' targets. if (bounds.Count > count) bounds.Dequeue(); } } } } } } return shortestPaths; }