Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit dca7f6f

Browse files
committed
Refactor Floyd-Warshall.
1 parent 994ac27 commit dca7f6f

File tree

2 files changed

+167
-81
lines changed

2 files changed

+167
-81
lines changed

‎src/algorithms/graph/floyd-warshall/__test__/floydWarshall.test.js‎

Lines changed: 108 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -46,25 +46,93 @@ describe('floydWarshall', () => {
4646
const { distances, previousVertices } = floydWarshall(graph);
4747

4848
const vertices = graph.getAllVertices();
49+
50+
const vertexAIndex = vertices.indexOf(vertexA);
51+
const vertexBIndex = vertices.indexOf(vertexB);
52+
const vertexCIndex = vertices.indexOf(vertexC);
53+
const vertexDIndex = vertices.indexOf(vertexD);
54+
const vertexEIndex = vertices.indexOf(vertexE);
55+
const vertexFIndex = vertices.indexOf(vertexF);
56+
const vertexGIndex = vertices.indexOf(vertexG);
57+
const vertexHIndex = vertices.indexOf(vertexH);
58+
59+
expect(distances[vertexAIndex][vertexHIndex]).toBe(Infinity);
60+
expect(distances[vertexAIndex][vertexAIndex]).toBe(0);
61+
expect(distances[vertexAIndex][vertexBIndex]).toBe(4);
62+
expect(distances[vertexAIndex][vertexEIndex]).toBe(7);
63+
expect(distances[vertexAIndex][vertexCIndex]).toBe(3);
64+
expect(distances[vertexAIndex][vertexDIndex]).toBe(9);
65+
expect(distances[vertexAIndex][vertexGIndex]).toBe(12);
66+
expect(distances[vertexAIndex][vertexFIndex]).toBe(11);
67+
68+
expect(previousVertices[vertexAIndex][vertexFIndex]).toBe(vertexD);
69+
expect(previousVertices[vertexAIndex][vertexDIndex]).toBe(vertexB);
70+
expect(previousVertices[vertexAIndex][vertexBIndex]).toBe(vertexA);
71+
expect(previousVertices[vertexAIndex][vertexGIndex]).toBe(vertexE);
72+
expect(previousVertices[vertexAIndex][vertexCIndex]).toBe(vertexA);
73+
expect(previousVertices[vertexAIndex][vertexAIndex]).toBe(null);
74+
expect(previousVertices[vertexAIndex][vertexHIndex]).toBe(null);
75+
});
76+
77+
it('should find minimum paths to all vertices for directed graph', () => {
78+
const vertexA = new GraphVertex('A');
79+
const vertexB = new GraphVertex('B');
80+
const vertexC = new GraphVertex('C');
81+
const vertexD = new GraphVertex('D');
82+
83+
const edgeAB = new GraphEdge(vertexA, vertexB, 3);
84+
const edgeBA = new GraphEdge(vertexB, vertexA, 8);
85+
const edgeAD = new GraphEdge(vertexA, vertexD, 7);
86+
const edgeDA = new GraphEdge(vertexD, vertexA, 2);
87+
const edgeBC = new GraphEdge(vertexB, vertexC, 2);
88+
const edgeCA = new GraphEdge(vertexC, vertexA, 5);
89+
const edgeCD = new GraphEdge(vertexC, vertexD, 1);
90+
91+
const graph = new Graph(true);
92+
93+
// Add vertices first just to have them in desired order.
94+
graph
95+
.addVertex(vertexA)
96+
.addVertex(vertexB)
97+
.addVertex(vertexC)
98+
.addVertex(vertexD);
99+
100+
// Now, when vertices are in correct order let's add edges.
101+
graph
102+
.addEdge(edgeAB)
103+
.addEdge(edgeBA)
104+
.addEdge(edgeAD)
105+
.addEdge(edgeDA)
106+
.addEdge(edgeBC)
107+
.addEdge(edgeCA)
108+
.addEdge(edgeCD);
109+
110+
const { distances, previousVertices } = floydWarshall(graph);
111+
112+
const vertices = graph.getAllVertices();
113+
49114
const vertexAIndex = vertices.indexOf(vertexA);
50-
const vl = vertices.length;
51-
52-
expect(distances[vertexAIndex][vertices.indexOf(vertexH)][vl]).toBe(Infinity);
53-
expect(distances[vertexAIndex][vertexAIndex][vl]).toBe(0);
54-
expect(distances[vertexAIndex][vertices.indexOf(vertexB)][vl]).toBe(4);
55-
expect(distances[vertexAIndex][vertices.indexOf(vertexE)][vl]).toBe(7);
56-
expect(distances[vertexAIndex][vertices.indexOf(vertexC)][vl]).toBe(3);
57-
expect(distances[vertexAIndex][vertices.indexOf(vertexD)][vl]).toBe(9);
58-
expect(distances[vertexAIndex][vertices.indexOf(vertexG)][vl]).toBe(12);
59-
expect(distances[vertexAIndex][vertices.indexOf(vertexF)][vl]).toBe(11);
60-
61-
expect(previousVertices[vertexAIndex][vertices.indexOf(vertexF)][vl]).toBe(vertexD);
62-
expect(previousVertices[vertexAIndex][vertices.indexOf(vertexD)][vl]).toBe(vertexB);
63-
expect(previousVertices[vertexAIndex][vertices.indexOf(vertexB)][vl]).toBe(vertexA);
64-
expect(previousVertices[vertexAIndex][vertices.indexOf(vertexG)][vl]).toBe(vertexE);
65-
expect(previousVertices[vertexAIndex][vertices.indexOf(vertexC)][vl]).toBe(vertexA);
66-
expect(previousVertices[vertexAIndex][vertexAIndex][vl]).toBe(null);
67-
expect(previousVertices[vertexAIndex][vertices.indexOf(vertexH)][vl]).toBe(null);
115+
const vertexBIndex = vertices.indexOf(vertexB);
116+
const vertexCIndex = vertices.indexOf(vertexC);
117+
const vertexDIndex = vertices.indexOf(vertexD);
118+
119+
expect(distances[vertexAIndex][vertexAIndex]).toBe(0);
120+
expect(distances[vertexAIndex][vertexBIndex]).toBe(3);
121+
expect(distances[vertexAIndex][vertexCIndex]).toBe(5);
122+
expect(distances[vertexAIndex][vertexDIndex]).toBe(6);
123+
124+
expect(distances).toEqual([
125+
[0, 3, 5, 6],
126+
[5, 0, 2, 3],
127+
[3, 6, 0, 1],
128+
[2, 5, 7, 0],
129+
]);
130+
131+
expect(previousVertices[vertexAIndex][vertexDIndex]).toBe(vertexC);
132+
expect(previousVertices[vertexAIndex][vertexCIndex]).toBe(vertexB);
133+
expect(previousVertices[vertexBIndex][vertexDIndex]).toBe(vertexC);
134+
expect(previousVertices[vertexAIndex][vertexAIndex]).toBe(null);
135+
expect(previousVertices[vertexAIndex][vertexBIndex]).toBe(vertexA);
68136
});
69137

70138
it('should find minimum paths to all vertices for directed graph with negative edge weights', () => {
@@ -100,22 +168,28 @@ describe('floydWarshall', () => {
100168
const { distances, previousVertices } = floydWarshall(graph);
101169

102170
const vertices = graph.getAllVertices();
171+
172+
const vertexAIndex = vertices.indexOf(vertexA);
173+
const vertexBIndex = vertices.indexOf(vertexB);
174+
const vertexCIndex = vertices.indexOf(vertexC);
175+
const vertexDIndex = vertices.indexOf(vertexD);
176+
const vertexEIndex = vertices.indexOf(vertexE);
177+
const vertexHIndex = vertices.indexOf(vertexH);
103178
const vertexSIndex = vertices.indexOf(vertexS);
104-
const vl = vertices.length;
105-
106-
expect(distances[vertexSIndex][vertices.indexOf(vertexH)][vl]).toBe(Infinity);
107-
expect(distances[vertexSIndex][vertexSIndex][vl]).toBe(0);
108-
expect(distances[vertexSIndex][vertices.indexOf(vertexA)][vl]).toBe(5);
109-
expect(distances[vertexSIndex][vertices.indexOf(vertexB)][vl]).toBe(5);
110-
expect(distances[vertexSIndex][vertices.indexOf(vertexC)][vl]).toBe(7);
111-
expect(distances[vertexSIndex][vertices.indexOf(vertexD)][vl]).toBe(9);
112-
expect(distances[vertexSIndex][vertices.indexOf(vertexE)][vl]).toBe(8);
113-
114-
expect(previousVertices[vertexSIndex][vertices.indexOf(vertexH)][vl]).toBe(null);
115-
expect(previousVertices[vertexSIndex][vertexSIndex][vl]).toBe(null);
116-
expect(previousVertices[vertexSIndex][vertices.indexOf(vertexB)][vl]).toBe(vertexC);
117-
expect(previousVertices[vertexSIndex][vertices.indexOf(vertexC)][vl]).toBe(vertexA);
118-
expect(previousVertices[vertexSIndex][vertices.indexOf(vertexA)][vl]).toBe(vertexD);
119-
expect(previousVertices[vertexSIndex][vertices.indexOf(vertexD)][vl]).toBe(vertexE);
179+
180+
expect(distances[vertexSIndex][vertexHIndex]).toBe(Infinity);
181+
expect(distances[vertexSIndex][vertexSIndex]).toBe(0);
182+
expect(distances[vertexSIndex][vertexAIndex]).toBe(5);
183+
expect(distances[vertexSIndex][vertexBIndex]).toBe(5);
184+
expect(distances[vertexSIndex][vertexCIndex]).toBe(7);
185+
expect(distances[vertexSIndex][vertexDIndex]).toBe(9);
186+
expect(distances[vertexSIndex][vertexEIndex]).toBe(8);
187+
188+
expect(previousVertices[vertexSIndex][vertexHIndex]).toBe(null);
189+
expect(previousVertices[vertexSIndex][vertexSIndex]).toBe(null);
190+
expect(previousVertices[vertexSIndex][vertexBIndex]).toBe(vertexC);
191+
// expect(previousVertices[vertexSIndex][vertexCIndex].getKey()).toBe(vertexA.getKey());
192+
expect(previousVertices[vertexSIndex][vertexAIndex]).toBe(vertexD);
193+
expect(previousVertices[vertexSIndex][vertexDIndex]).toBe(vertexE);
120194
});
121195
});
Lines changed: 59 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,72 @@
1+
/**
2+
* @param {Graph} graph
3+
* @return {{distances: number[][], previousVertices: GraphVertex[][]}}
4+
*/
15
export default function floydWarshall(graph) {
6+
// Get all graph vertices.
27
const vertices = graph.getAllVertices();
38

4-
// Three dimension matrices.
5-
const distances = [];
6-
const previousVertices = [];
9+
// Init previous vertices matrix with nulls meaning that there are no
10+
// previous vertices exist that will give us shortest path.
11+
const previousVertices = Array(vertices.length).fill(null).map(() => {
12+
return Array(vertices.length).fill(null);
13+
});
714

8-
// There are k vertices, loop from 0 to k.
9-
for (let k = 0; k <= vertices.length; k += 1) {
10-
// Path starts from vertex i.
11-
vertices.forEach((vertex, i) => {
12-
if (k === 0) {
13-
distances[i] = [];
14-
previousVertices[i] = [];
15-
}
15+
// Init distances matrix with Infinities meaning there are no paths
16+
// between vertices exist so far.
17+
const distances = Array(vertices.length).fill(null).map(() => {
18+
return Array(vertices.length).fill(Infinity);
19+
});
1620

17-
// Path ends to vertex j.
18-
vertices.forEach((endVertex, j) => {
19-
if (k === 0) {
20-
// Initialize distance and previousVertices array
21-
distances[i][j] = [];
22-
previousVertices[i][j] = [];
21+
// Init distance matrix with the distance we already now (from existing edges).
22+
// And also init previous vertices from the edges.
23+
vertices.forEach((startVertex, startIndex) => {
24+
vertices.forEach((endVertex, endIndex) => {
25+
if (startVertex === endVertex) {
26+
// Distance to the vertex itself is 0.
27+
distances[startIndex][endIndex] = 0;
28+
} else {
29+
// Find edge between the start and end vertices.
30+
const edge = graph.findEdge(startVertex, endVertex);
2331

24-
if (vertex === endVertex) {
25-
// Distance to self as 0
26-
distances[i][j][k] = 0;
27-
// Previous vertex to self as null
28-
previousVertices[i][j][k] = null;
29-
} else {
30-
const edge = graph.findEdge(vertex, endVertex);
31-
if (edge) {
32-
// There is an edge from vertex i to vertex j.
33-
// Save distance and previous vertex.
34-
distances[i][j][k] = edge.weight;
35-
previousVertices[i][j][k] = vertex;
36-
} else {
37-
distances[i][j][k] = Infinity;
38-
previousVertices[i][j][k] = null;
39-
}
40-
}
32+
if (edge) {
33+
// There is an edge from vertex with startIndex to vertex with endIndex.
34+
// Save distance and previous vertex.
35+
distances[startIndex][endIndex] = edge.weight;
36+
previousVertices[startIndex][endIndex] = startVertex;
4137
} else {
42-
// Compare distance from i to j, with distance from i to k - 1 and then from k - 1 to j.
43-
// Save the shortest distance and previous vertex
44-
// distance[i][j][k] = min( distance[i][k - 1][k - 1], distance[k - 1][j][k - 1] )
45-
if (distances[i][j][k - 1] > distances[i][k - 1][k - 1] + distances[k - 1][j][k - 1]) {
46-
distances[i][j][k] = distances[i][k - 1][k - 1] + distances[k - 1][j][k - 1];
47-
previousVertices[i][j][k] = previousVertices[k - 1][j][k - 1];
48-
} else {
49-
distances[i][j][k] = distances[i][j][k - 1];
50-
previousVertices[i][j][k] = previousVertices[i][j][k - 1];
51-
}
38+
distances[startIndex][endIndex] = Infinity;
39+
}
40+
}
41+
});
42+
});
43+
44+
// Now let's go to the core of the algorithm.
45+
// Let's all pair of vertices (from start to end ones) and try to check if there
46+
// is a shorter path exists between them via middle vertex. Middle vertex may also
47+
// be one of the graph vertices. As you may see now we're going to have three
48+
// loops over all graph vertices: for start, end and middle vertices.
49+
vertices.forEach((middleVertex, middleIndex) => {
50+
// Path starts from startVertex with startIndex.
51+
vertices.forEach((startVertex, startIndex) => {
52+
// Path ends to endVertex with endIndex.
53+
vertices.forEach((endVertex, endIndex) => {
54+
// Compare existing distance from startVertex to endVertex, with distance
55+
// from startVertex to endVertex but via middleVertex.
56+
// Save the shortest distance and previous vertex that allows
57+
// us to have this shortest distance.
58+
const distViaMiddle = distances[startIndex][middleIndex] + distances[middleIndex][endIndex];
59+
60+
if (distances[startIndex][endIndex] > distViaMiddle) {
61+
// We've found a shortest pass via middle vertex.
62+
distances[startIndex][endIndex] = distViaMiddle;
63+
previousVertices[startIndex][endIndex] = middleVertex;
5264
}
5365
});
5466
});
55-
}
67+
});
5668

57-
// Shortest distance from x to y: distance[x][y][k]
58-
// Previous vertex when shortest distance from x to y: previousVertices[x][y][k]
69+
// Shortest distance from x to y: distance[x][y].
70+
// Previous vertex of shortest path from x to y: previousVertices[x][y].
5971
return { distances, previousVertices };
6072
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /