From 6748c2c89d0b2625c72358902ab9c6badf1d556b Mon Sep 17 00:00:00 2001 From: Shubham Padkonde Date: Sat, 24 Oct 2026 10:45:40 +0530 Subject: [PATCH] fix(floyd-warshall): preserve shortest-path predecessors --- .../__test__/floydWarshall.test.js | 41 ++++++++++++++++++- .../graph/floyd-warshall/floydWarshall.js | 4 +- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/algorithms/graph/floyd-warshall/__test__/floydWarshall.test.js b/src/algorithms/graph/floyd-warshall/__test__/floydWarshall.test.js index dea170c61a..d2dec79d0f 100644 --- a/src/algorithms/graph/floyd-warshall/__test__/floydWarshall.test.js +++ b/src/algorithms/graph/floyd-warshall/__test__/floydWarshall.test.js @@ -4,6 +4,38 @@ import Graph from '../../../../data-structures/graph/Graph'; import floydWarshall from '../floydWarshall'; describe('floydWarshall', () => { + it.each([false, true])('should reconstruct paths through a multi-edge suffix (directed: %s)', (directed) => { + const vertexA = new GraphVertex('A'); + const vertexB = new GraphVertex('B'); + const vertexC = new GraphVertex('C'); + const vertexD = new GraphVertex('D'); + const graph = new Graph(directed); + + // Discover D -> B -> C before considering D as an intermediate vertex. + [vertexB, vertexC, vertexA, vertexD].forEach((vertex) => graph.addVertex(vertex)); + graph + .addEdge(new GraphEdge(vertexA, vertexD, 2)) + .addEdge(new GraphEdge(vertexD, vertexB, 3)) + .addEdge(new GraphEdge(vertexB, vertexC, 4)); + + const { distances, nextVertices } = floydWarshall(graph); + const indices = graph.getVerticesIndices(); + expect(distances[indices.A][indices.C]).toBe(9); + expect(nextVertices[indices.A][indices.C]).toBe(vertexB); + + const path = [vertexC]; + let weight = 0; + while (path[0] !== vertexA && path.length <= graph.getAllVertices().length) { + const previous = nextVertices[indices.A][indices[path[0].getKey()]]; + const edge = graph.findEdge(previous, path[0]); + expect(edge).toBeDefined(); + weight += edge.weight; + path.unshift(previous); + } + expect(path).toEqual([vertexA, vertexD, vertexB, vertexC]); + expect(weight).toBe(distances[indices.A][indices.C]); + }); + it('should find minimum paths to all vertices for undirected graph', () => { const vertexA = new GraphVertex('A'); const vertexB = new GraphVertex('B'); @@ -212,8 +244,13 @@ describe('floydWarshall', () => { expect(nextVertices[vertexFIndex][vertexFIndex]).toBe(null); expect(nextVertices[vertexAIndex][vertexBIndex]).toBe(vertexC); expect(nextVertices[vertexAIndex][vertexCIndex]).toBe(vertexA); - expect(nextVertices[vertexFIndex][vertexBIndex]).toBe(vertexE); - expect(nextVertices[vertexEIndex][vertexBIndex]).toBe(vertexD); + // The shortest F -> B path is F -> E -> D -> A -> C -> B. + expect(nextVertices[vertexFIndex][vertexBIndex]).toBe(vertexC); + expect(nextVertices[vertexFIndex][vertexCIndex]).toBe(vertexA); + expect(nextVertices[vertexFIndex][vertexAIndex]).toBe(vertexD); + expect(nextVertices[vertexFIndex][vertexDIndex]).toBe(vertexE); + expect(nextVertices[vertexFIndex][vertexEIndex]).toBe(vertexF); + expect(nextVertices[vertexEIndex][vertexBIndex]).toBe(vertexC); expect(nextVertices[vertexDIndex][vertexBIndex]).toBe(vertexC); expect(nextVertices[vertexCIndex][vertexBIndex]).toBe(vertexC); }); diff --git a/src/algorithms/graph/floyd-warshall/floydWarshall.js b/src/algorithms/graph/floyd-warshall/floydWarshall.js index 870d78a6e8..19d795e7eb 100644 --- a/src/algorithms/graph/floyd-warshall/floydWarshall.js +++ b/src/algorithms/graph/floyd-warshall/floydWarshall.js @@ -60,13 +60,13 @@ export default function floydWarshall(graph) { if (distances[startIndex][endIndex] > distViaMiddle) { // We've found a shortest pass via middle vertex. distances[startIndex][endIndex] = distViaMiddle; - nextVertices[startIndex][endIndex] = middleVertex; + nextVertices[startIndex][endIndex] = nextVertices[middleIndex][endIndex]; } }); }); }); // Shortest distance from x to y: distance[x][y]. - // Next vertex after x one in path from x to y: nextVertices[x][y]. + // Previous vertex before y in the shortest path from x to y: nextVertices[x][y]. return { distances, nextVertices }; }