feat(algorithms): Add Floyd-Warshall (#97)

This commit is contained in:
vivaxy 2018-07-13 19:23:47 +08:00 committed by Oleksii Trekhleb
parent 3e8540beac
commit 9f8fd33202
6 changed files with 194 additions and 5 deletions

View File

@ -113,7 +113,8 @@ a set of rules that precisely define a sequence of operations.
* `A` [Hamiltonian Cycle](src/algorithms/graph/hamiltonian-cycle) - Visit every vertex exactly once * `A` [Hamiltonian Cycle](src/algorithms/graph/hamiltonian-cycle) - Visit every vertex exactly once
* `A` [Strongly Connected Components](src/algorithms/graph/strongly-connected-components) - Kosaraju's algorithm * `A` [Strongly Connected Components](src/algorithms/graph/strongly-connected-components) - Kosaraju's algorithm
* `A` [Travelling Salesman Problem](src/algorithms/graph/travelling-salesman) - shortest possible route that visits each city and returns to the origin city * `A` [Travelling Salesman Problem](src/algorithms/graph/travelling-salesman) - shortest possible route that visits each city and returns to the origin city
* **Uncategorized** * `A` [Floyd-Warshall algorithm](src/algorithms/graph/floyd-warshall) - a single execution of the algorithm will find the lengths (summed weights) of shortest paths between all pairs of vertices
* **Uncategorized**
* `B` [Tower of Hanoi](src/algorithms/uncategorized/hanoi-tower) * `B` [Tower of Hanoi](src/algorithms/uncategorized/hanoi-tower)
* `B` [Square Matrix Rotation](src/algorithms/uncategorized/square-matrix-rotation) - in-place algorithm * `B` [Square Matrix Rotation](src/algorithms/uncategorized/square-matrix-rotation) - in-place algorithm
* `B` [Jump Game](src/algorithms/uncategorized/jump-game) - backtracking, dynamic programming (top-down + bottom-up) and greedy examples * `B` [Jump Game](src/algorithms/uncategorized/jump-game) - backtracking, dynamic programming (top-down + bottom-up) and greedy examples

View File

@ -69,7 +69,7 @@ _Read this in other languages:_
* [归并排序](src/algorithms/sorting/merge-sort) * [归并排序](src/algorithms/sorting/merge-sort)
* [快速排序](src/algorithms/sorting/quick-sort) * [快速排序](src/algorithms/sorting/quick-sort)
* [希尔排序](src/algorithms/sorting/shell-sort) * [希尔排序](src/algorithms/sorting/shell-sort)
* **树** * **树**
* [深度优先搜索](src/algorithms/tree/depth-first-search) (DFS) * [深度优先搜索](src/algorithms/tree/depth-first-search) (DFS)
* [广度优先搜索](src/algorithms/tree/breadth-first-search) (BFS) * [广度优先搜索](src/algorithms/tree/breadth-first-search) (BFS)
* **图** * **图**
@ -87,7 +87,8 @@ _Read this in other languages:_
* [哈密顿图](src/algorithms/graph/hamiltonian-cycle) - 恰好访问每个顶点一次 * [哈密顿图](src/algorithms/graph/hamiltonian-cycle) - 恰好访问每个顶点一次
* [强连通分量](src/algorithms/graph/strongly-connected-components) - Kosaraju算法 * [强连通分量](src/algorithms/graph/strongly-connected-components) - Kosaraju算法
* [旅行推销员问题](src/algorithms/graph/travelling-salesman) - 尽可能以最短的路线访问每个城市并返回原始城市 * [旅行推销员问题](src/algorithms/graph/travelling-salesman) - 尽可能以最短的路线访问每个城市并返回原始城市
* **未分类** * [Floyd-Warshall algorithm](src/algorithms/graph/floyd-warshall) - 一次循环可以找出所有顶点之间的最短路径
* **未分类**
* [汉诺塔](src/algorithms/uncategorized/hanoi-tower) * [汉诺塔](src/algorithms/uncategorized/hanoi-tower)
* [八皇后问题](src/algorithms/uncategorized/n-queens) * [八皇后问题](src/algorithms/uncategorized/n-queens)
* [骑士巡逻](src/algorithms/uncategorized/knight-tour) * [骑士巡逻](src/algorithms/uncategorized/knight-tour)

View File

@ -68,7 +68,7 @@ _Read this in other languages:_
* [合併排序](src/algorithms/sorting/merge-sort) * [合併排序](src/algorithms/sorting/merge-sort)
* [快速排序](src/algorithms/sorting/quick-sort) * [快速排序](src/algorithms/sorting/quick-sort)
* [希爾排序](src/algorithms/sorting/shell-sort) * [希爾排序](src/algorithms/sorting/shell-sort)
* **樹** * **樹**
* [深度優先搜尋](src/algorithms/tree/depth-first-search) (DFS) * [深度優先搜尋](src/algorithms/tree/depth-first-search) (DFS)
* [廣度優先搜尋](src/algorithms/tree/breadth-first-search) (BFS) * [廣度優先搜尋](src/algorithms/tree/breadth-first-search) (BFS)
* **圖** * **圖**
@ -86,7 +86,8 @@ _Read this in other languages:_
* [漢彌爾頓環](src/algorithms/graph/hamiltonian-cycle) - Visit every vertex exactly once * [漢彌爾頓環](src/algorithms/graph/hamiltonian-cycle) - Visit every vertex exactly once
* [強連通組件](src/algorithms/graph/strongly-connected-components) - Kosaraju's algorithm * [強連通組件](src/algorithms/graph/strongly-connected-components) - Kosaraju's algorithm
* [旅行推銷員問題](src/algorithms/graph/travelling-salesman) - shortest possible route that visits each city and returns to the origin city * [旅行推銷員問題](src/algorithms/graph/travelling-salesman) - shortest possible route that visits each city and returns to the origin city
* **未分類** * [Floyd-Warshall algorithm](src/algorithms/graph/floyd-warshall) - 一次循环可以找出所有頂點之间的最短路徑
* **未分類**
* [河內塔](src/algorithms/uncategorized/hanoi-tower) * [河內塔](src/algorithms/uncategorized/hanoi-tower)
* [N-皇后問題](src/algorithms/uncategorized/n-queens) * [N-皇后問題](src/algorithms/uncategorized/n-queens)
* [騎士走棋盤](src/algorithms/uncategorized/knight-tour) * [騎士走棋盤](src/algorithms/uncategorized/knight-tour)

View File

@ -0,0 +1,5 @@
# FloydWarshall algorithm
## References
- [Wikipedia](https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm)

View File

@ -0,0 +1,121 @@
import GraphVertex from '../../../../data-structures/graph/GraphVertex';
import GraphEdge from '../../../../data-structures/graph/GraphEdge';
import Graph from '../../../../data-structures/graph/Graph';
import floydWarshall from '../floydWarshall';
describe('floydWarshall', () => {
it('should find minimum paths to all vertices for undirected graph', () => {
const vertexA = new GraphVertex('A');
const vertexB = new GraphVertex('B');
const vertexC = new GraphVertex('C');
const vertexD = new GraphVertex('D');
const vertexE = new GraphVertex('E');
const vertexF = new GraphVertex('F');
const vertexG = new GraphVertex('G');
const vertexH = new GraphVertex('H');
const edgeAB = new GraphEdge(vertexA, vertexB, 4);
const edgeAE = new GraphEdge(vertexA, vertexE, 7);
const edgeAC = new GraphEdge(vertexA, vertexC, 3);
const edgeBC = new GraphEdge(vertexB, vertexC, 6);
const edgeBD = new GraphEdge(vertexB, vertexD, 5);
const edgeEC = new GraphEdge(vertexE, vertexC, 8);
const edgeED = new GraphEdge(vertexE, vertexD, 2);
const edgeDC = new GraphEdge(vertexD, vertexC, 11);
const edgeDG = new GraphEdge(vertexD, vertexG, 10);
const edgeDF = new GraphEdge(vertexD, vertexF, 2);
const edgeFG = new GraphEdge(vertexF, vertexG, 3);
const edgeEG = new GraphEdge(vertexE, vertexG, 5);
const graph = new Graph();
graph
.addVertex(vertexH)
.addEdge(edgeAB)
.addEdge(edgeAE)
.addEdge(edgeAC)
.addEdge(edgeBC)
.addEdge(edgeBD)
.addEdge(edgeEC)
.addEdge(edgeED)
.addEdge(edgeDC)
.addEdge(edgeDG)
.addEdge(edgeDF)
.addEdge(edgeFG)
.addEdge(edgeEG);
const { distances, previousVertices } = floydWarshall(graph);
const vertices = graph.getAllVertices();
const vertexAIndex = vertices.indexOf(vertexA);
const vl = vertices.length;
expect(distances[vertexAIndex][vertices.indexOf(vertexH)][vl]).toBe(Infinity);
expect(distances[vertexAIndex][vertexAIndex][vl]).toBe(0);
expect(distances[vertexAIndex][vertices.indexOf(vertexB)][vl]).toBe(4);
expect(distances[vertexAIndex][vertices.indexOf(vertexE)][vl]).toBe(7);
expect(distances[vertexAIndex][vertices.indexOf(vertexC)][vl]).toBe(3);
expect(distances[vertexAIndex][vertices.indexOf(vertexD)][vl]).toBe(9);
expect(distances[vertexAIndex][vertices.indexOf(vertexG)][vl]).toBe(12);
expect(distances[vertexAIndex][vertices.indexOf(vertexF)][vl]).toBe(11);
expect(previousVertices[vertexAIndex][vertices.indexOf(vertexF)][vl]).toBe(vertexD);
expect(previousVertices[vertexAIndex][vertices.indexOf(vertexD)][vl]).toBe(vertexB);
expect(previousVertices[vertexAIndex][vertices.indexOf(vertexB)][vl]).toBe(vertexA);
expect(previousVertices[vertexAIndex][vertices.indexOf(vertexG)][vl]).toBe(vertexE);
expect(previousVertices[vertexAIndex][vertices.indexOf(vertexC)][vl]).toBe(vertexA);
expect(previousVertices[vertexAIndex][vertexAIndex][vl]).toBe(null);
expect(previousVertices[vertexAIndex][vertices.indexOf(vertexH)][vl]).toBe(null);
});
it('should find minimum paths to all vertices for directed graph with negative edge weights', () => {
const vertexS = new GraphVertex('S');
const vertexE = new GraphVertex('E');
const vertexA = new GraphVertex('A');
const vertexD = new GraphVertex('D');
const vertexB = new GraphVertex('B');
const vertexC = new GraphVertex('C');
const vertexH = new GraphVertex('H');
const edgeSE = new GraphEdge(vertexS, vertexE, 8);
const edgeSA = new GraphEdge(vertexS, vertexA, 10);
const edgeED = new GraphEdge(vertexE, vertexD, 1);
const edgeDA = new GraphEdge(vertexD, vertexA, -4);
const edgeDC = new GraphEdge(vertexD, vertexC, -1);
const edgeAC = new GraphEdge(vertexA, vertexC, 2);
const edgeCB = new GraphEdge(vertexC, vertexB, -2);
const edgeBA = new GraphEdge(vertexB, vertexA, 1);
const graph = new Graph(true);
graph
.addVertex(vertexH)
.addEdge(edgeSE)
.addEdge(edgeSA)
.addEdge(edgeED)
.addEdge(edgeDA)
.addEdge(edgeDC)
.addEdge(edgeAC)
.addEdge(edgeCB)
.addEdge(edgeBA);
const { distances, previousVertices } = floydWarshall(graph);
const vertices = graph.getAllVertices();
const vertexSIndex = vertices.indexOf(vertexS);
const vl = vertices.length;
expect(distances[vertexSIndex][vertices.indexOf(vertexH)][vl]).toBe(Infinity);
expect(distances[vertexSIndex][vertexSIndex][vl]).toBe(0);
expect(distances[vertexSIndex][vertices.indexOf(vertexA)][vl]).toBe(5);
expect(distances[vertexSIndex][vertices.indexOf(vertexB)][vl]).toBe(5);
expect(distances[vertexSIndex][vertices.indexOf(vertexC)][vl]).toBe(7);
expect(distances[vertexSIndex][vertices.indexOf(vertexD)][vl]).toBe(9);
expect(distances[vertexSIndex][vertices.indexOf(vertexE)][vl]).toBe(8);
expect(previousVertices[vertexSIndex][vertices.indexOf(vertexH)][vl]).toBe(null);
expect(previousVertices[vertexSIndex][vertexSIndex][vl]).toBe(null);
expect(previousVertices[vertexSIndex][vertices.indexOf(vertexB)][vl]).toBe(vertexC);
expect(previousVertices[vertexSIndex][vertices.indexOf(vertexC)][vl]).toBe(vertexA);
expect(previousVertices[vertexSIndex][vertices.indexOf(vertexA)][vl]).toBe(vertexD);
expect(previousVertices[vertexSIndex][vertices.indexOf(vertexD)][vl]).toBe(vertexE);
});
});

View File

@ -0,0 +1,60 @@
export default function floydWarshall(graph) {
const vertices = graph.getAllVertices();
// Three dimension matrices.
const distances = [];
const previousVertices = [];
// There are k vertices, loop from 0 to k.
for (let k = 0; k <= vertices.length; k += 1) {
// Path starts from vertex i.
vertices.forEach((vertex, i) => {
if (k === 0) {
distances[i] = [];
previousVertices[i] = [];
}
// Path ends to vertex j.
vertices.forEach((endVertex, j) => {
if (k === 0) {
// Initialize distance and previousVertices array
distances[i][j] = [];
previousVertices[i][j] = [];
if (vertex === endVertex) {
// Distance to self as 0
distances[i][j][k] = 0;
// Previous vertex to self as null
previousVertices[i][j][k] = null;
} else {
const edge = graph.findEdge(vertex, endVertex);
if (edge) {
// There is an edge from vertex i to vertex j.
// Save distance and previous vertex.
distances[i][j][k] = edge.weight;
previousVertices[i][j][k] = vertex;
} else {
distances[i][j][k] = Infinity;
previousVertices[i][j][k] = null;
}
}
} else {
// Compare distance from i to j, with distance from i to k - 1 and then from k - 1 to j.
// Save the shortest distance and previous vertex
// distance[i][j][k] = min( distance[i][k - 1][k - 1], distance[k - 1][j][k - 1] )
if (distances[i][j][k - 1] > distances[i][k - 1][k - 1] + distances[k - 1][j][k - 1]) {
distances[i][j][k] = distances[i][k - 1][k - 1] + distances[k - 1][j][k - 1];
previousVertices[i][j][k] = previousVertices[k - 1][j][k - 1];
} else {
distances[i][j][k] = distances[i][j][k - 1];
previousVertices[i][j][k] = previousVertices[i][j][k - 1];
}
}
});
});
}
// Shortest distance from x to y: distance[x][y][k]
// Previous vertex when shortest distance from x to y: previousVertices[x][y][k]
return { distances, previousVertices };
}