( graph: [number, number][][] )
| 12 | * @see https://en.wikipedia.org/wiki/Johnson%27s_algorithm |
| 13 | */ |
| 14 | export const johnson = ( |
| 15 | graph: [number, number][][] |
| 16 | ): number[][] | undefined => { |
| 17 | const N = graph.length |
| 18 | |
| 19 | // Add a new node and 0 weighted edges from the new node to all existing nodes. |
| 20 | const newNodeGraph = structuredClone(graph) |
| 21 | const newNode: [number, number][] = [] |
| 22 | for (let i = 0; i < N; ++i) { |
| 23 | newNode.push([i, 0]) |
| 24 | } |
| 25 | newNodeGraph.push(newNode) |
| 26 | |
| 27 | // Compute distances from the new node to existing nodes using the Bellman-Ford algorithm. |
| 28 | const adjustedGraph = bellmanFord(newNodeGraph, N) |
| 29 | if (adjustedGraph === undefined) { |
| 30 | // Found a negative weight cycle. |
| 31 | return undefined |
| 32 | } |
| 33 | |
| 34 | for (let i = 0; i < N; ++i) { |
| 35 | for (const edge of graph[i]) { |
| 36 | // Adjust edge weights using the Bellman Ford output weights. This ensure that: |
| 37 | // 1. Each weight is non-negative. This is required for the Dijkstra algorithm. |
| 38 | // 2. The shortest path from node i to node j consists of the same nodes with or without adjustment. |
| 39 | edge[1] += adjustedGraph[i] - adjustedGraph[edge[0]] |
| 40 | } |
| 41 | } |
| 42 | |
| 43 | const shortestPaths: number[][] = [] |
| 44 | for (let i = 0; i < N; ++i) { |
| 45 | // Compute Dijkstra weights for each node and re-adjust weights to their original values. |
| 46 | const dijkstraShorestPaths = dijkstra(graph, i) |
| 47 | for (let j = 0; j < N; ++j) { |
| 48 | dijkstraShorestPaths[j] += adjustedGraph[j] - adjustedGraph[i] |
| 49 | } |
| 50 | shortestPaths.push(dijkstraShorestPaths) |
| 51 | } |
| 52 | return shortestPaths |
| 53 | } |
no test coverage detected