UNPKG

modern-dijkstra

Version:

A modern JavaScript implementation of Dijkstra's single-source shortest-paths algorithm.

111 lines (97 loc) 4.67 kB
/****************************************************************************** * Created 2008-08-19. * * Dijkstra path-finding functions. Adapted from the Dijkstar Python project. * * Copyright (C) 2021 * James McIntosh <github@ProfDeCube.com> * All rights reserved * * Licensed under the MIT license. * * http://www.opensource.org/licenses/mit-license.php * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. *****************************************************************************/ let queue = []; const addToQueue = (value, cost) => { queue.push({ value, cost }); queue.sort((a, b) => b.cost - a.cost); } const singleSourceShortestPaths = (graph, start, destination) => { // Predecessor map for each node that has been encountered. // node ID => predecessor node ID const predecessors = {}; // Costs of shortest paths from s to all nodes encountered. // node ID => cost const costs = {}; costs[start] = 0; // Costs of shortest paths from s to all nodes encountered; differs from // `costs` in that it provides easy access to the node that currently has // the known shortest path from s. // XXX: Do we actually need both `costs` and `open`? queue = []; addToQueue(start, 0); while (queue.length) { // In the nodes remaining in graph that have a known cost from s, // find the node, closestValue, that currently has the shortest path from s. const closest = queue.pop(); const shortestValue = closest.value; const shortestCost = closest.cost; // Get nodes adjacent to closestValue... const adjacentNodes = graph[shortestValue] || {}; // ...and explore the edges that connect closestValue to those nodes, updating // the cost of the shortest paths to any or all of those nodes as // necessary. adjacentNode is the node across the current edge from closestValue. for (const adjacentNode in adjacentNodes) { if (adjacentNodes.hasOwnProperty(adjacentNode)) { // Get the cost of the edge running from closestValue to adjacentNode. const adjacentNodeCost = adjacentNodes[adjacentNode]; // Cost of s to closestValue plus the cost of closestValue to adjacentNode across e--this is *a* // cost from s to adjacentNode that may or may not be less than the current // known cost to adjacentNode. const totalCostToAdjecentNode = shortestCost + adjacentNodeCost; // If we haven't visited adjacentNode yet OR if the current known cost from s to // adjacentNode is greater than the new cost we just found (cost of s to closestValue plus // cost of closestValue to adjacentNode across e), update adjacentNode's cost in the cost list and // update adjacentNode's predecessor in the predecessor list (it's now closestValue). const cost_of_s_to_v = costs[adjacentNode]; const first_visit = (typeof costs[adjacentNode] === 'undefined'); if (first_visit || cost_of_s_to_v > totalCostToAdjecentNode) { costs[adjacentNode] = totalCostToAdjecentNode; addToQueue(adjacentNode, totalCostToAdjecentNode); predecessors[adjacentNode] = shortestValue; } } } } if (typeof destination !== 'undefined' && typeof costs[destination] === 'undefined') { throw new Error(`Could not find a path from ${start} to ${destination}.`); } return [predecessors, costs]; } const extractShortestPathFromPredecessorList = (predecessors, destination) => { const nodes = []; let closestValue = destination; while (closestValue) { nodes.push(closestValue); closestValue = predecessors[closestValue]; } nodes.reverse(); return nodes; } export const findPath = (graph, start, destination) => { return findPathWithCost(graph, start, destination)[0]; } export const findPathWithCost = (graph, start, destination) => { const [predecessors, costs] = singleSourceShortestPaths(graph, start, destination); return [extractShortestPathFromPredecessorList( predecessors, destination), costs[destination]]; } export default { findPath, findPathWithCost, singleSourceShortestPaths }