Skip to content

Commit 415a9b4

Browse files
author
FSou1
committed
Add the Bellman-Ford algorithm
1 parent 5c5a124 commit 415a9b4

File tree

6 files changed

+195
-1
lines changed

6 files changed

+195
-1
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Bellman-Ford
2+
3+
## Applications
4+
5+
Same as the others algorithms (e.g. Dijkstra) for finding the shortest path:
6+
7+
* Searching for a shortest path
8+
* Build an optimal path at geographical maps
9+
* Network routing protocols
10+
11+
Moreover, due to its ability to work with negative edges, the algorithm is also used for:
12+
13+
* Finding negative cycles
14+
15+
## Complexity
16+
17+
**Time Complexity**: `O(|V| * |E|)` since the algorithm performs one outer (over vertices) and one inner (over edges) loop.
18+
19+
**Space Complexity**: `O(|V|)` since the algorithm persists two dictionaries of length `|V|`.
20+
21+
## References
22+
23+
- [Youtube](https://www.youtube.com/watch?v=ozsuci5pIso);
24+
- [Wikipedia](https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import {GraphVertex} from '../../../../data-structures/graph/graphVertex';
2+
import {GraphEdge} from '../../../../data-structures/graph/graphEdge';
3+
import {Graph} from '../../../../data-structures/graph/graph';
4+
import {bellmanFord} from '../bellmanFord';
5+
6+
test('should return shortest path to vertices', () => {
7+
const vertexA = new GraphVertex('A');
8+
const vertexB = new GraphVertex('B');
9+
const vertexC = new GraphVertex('C');
10+
const vertexD = new GraphVertex('D');
11+
const vertexE = new GraphVertex('E');
12+
const vertexF = new GraphVertex('F');
13+
const vertexG = new GraphVertex('G');
14+
const vertexH = new GraphVertex('H');
15+
16+
const edgeAB = new GraphEdge(vertexA, vertexB, 4);
17+
const edgeAE = new GraphEdge(vertexA, vertexE, 7);
18+
const edgeAC = new GraphEdge(vertexA, vertexC, 3);
19+
const edgeBC = new GraphEdge(vertexB, vertexC, 6);
20+
const edgeBD = new GraphEdge(vertexB, vertexD, 5);
21+
const edgeEC = new GraphEdge(vertexE, vertexC, 8);
22+
const edgeED = new GraphEdge(vertexE, vertexD, 2);
23+
const edgeDC = new GraphEdge(vertexD, vertexC, 11);
24+
const edgeDG = new GraphEdge(vertexD, vertexG, 10);
25+
const edgeDF = new GraphEdge(vertexD, vertexF, 2);
26+
const edgeFG = new GraphEdge(vertexF, vertexG, 3);
27+
const edgeEG = new GraphEdge(vertexE, vertexG, 5);
28+
29+
const graph = new Graph(true);
30+
graph
31+
.addVertex(vertexH)
32+
.addEdge(edgeAB)
33+
.addEdge(edgeAE)
34+
.addEdge(edgeAC)
35+
.addEdge(edgeBC)
36+
.addEdge(edgeBD)
37+
.addEdge(edgeEC)
38+
.addEdge(edgeED)
39+
.addEdge(edgeDC)
40+
.addEdge(edgeDG)
41+
.addEdge(edgeDF)
42+
.addEdge(edgeFG)
43+
.addEdge(edgeEG);
44+
45+
const {dist, prev} = bellmanFord(graph, vertexA);
46+
47+
expect(dist['H']).toBe(Infinity);
48+
expect(dist['A']).toBe(0);
49+
expect(dist['B']).toBe(4);
50+
expect(dist['E']).toBe(7);
51+
expect(dist['C']).toBe(3);
52+
expect(dist['D']).toBe(9);
53+
expect(dist['G']).toBe(12);
54+
expect(dist['F']).toBe(11);
55+
56+
expect(prev['F']).toBe('D');
57+
expect(prev['D']).toBe('B');
58+
expect(prev['B']).toBe('A');
59+
expect(prev['G']).toBe('E');
60+
expect(prev['C']).toBe('A');
61+
expect(prev['A']).toBeUndefined();
62+
expect(prev['H']).toBeUndefined();
63+
});
64+
65+
test('should throw a negative cycle exception', () => {
66+
const vertexA = new GraphVertex('A');
67+
const vertexB = new GraphVertex('B');
68+
const vertexC = new GraphVertex('C');
69+
const vertexD = new GraphVertex('D');
70+
71+
const edgeAB = new GraphEdge(vertexA, vertexB, 5);
72+
const edgeBC = new GraphEdge(vertexB, vertexC, 3);
73+
const edgeCD = new GraphEdge(vertexC, vertexD, 2);
74+
const edgeAD = new GraphEdge(vertexA, vertexD, 4);
75+
const edgeDB = new GraphEdge(vertexD, vertexB, -6);
76+
77+
const graph = new Graph(true);
78+
graph
79+
.addEdge(edgeAB)
80+
.addEdge(edgeBC)
81+
.addEdge(edgeCD)
82+
.addEdge(edgeAD)
83+
.addEdge(edgeDB);
84+
85+
expect(() => {
86+
bellmanFord(graph, vertexA);
87+
}).toThrowError(new Error('Graph contains a negative-weight cycle'));
88+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/* eslint-disable no-unused-vars */
2+
import {Graph} from '../../../data-structures/graph/graph';
3+
import {GraphVertex} from '../../../data-structures/graph/graphVertex';
4+
5+
/**
6+
*
7+
*
8+
* @export
9+
* @param {Graph} graph
10+
* @param {GraphVertex} source
11+
* @return {{
12+
* dist: Map<string, number>,
13+
* prev: Map<string, string>
14+
* }}
15+
*/
16+
export function bellmanFord(
17+
graph: Graph,
18+
source: GraphVertex,
19+
): {
20+
dist: Map<string, number>;
21+
prev: Map<string, string>;
22+
} {
23+
const dist = new Map<string, number>();
24+
const prev = new Map<string, string>();
25+
26+
const vertices = graph.getVertices();
27+
for (const vertex of vertices) {
28+
dist[vertex.getKey()] = Infinity;
29+
prev[vertex.getKey()] = undefined;
30+
}
31+
dist[source.getKey()] = 0;
32+
33+
const edges = graph.getEdges();
34+
for (let i = 0, j = vertices.length - 1; i < j; i++) {
35+
for (const edge of edges) {
36+
const u = edge.startVertex.getKey();
37+
const v = edge.endVertex.getKey();
38+
const weight = edge.weight;
39+
40+
if (dist[u] + weight < dist[v]) {
41+
// a new s.p. has been found
42+
dist[v] = dist[u] + weight;
43+
prev[v] = u;
44+
}
45+
}
46+
}
47+
48+
// check for negative weight cycles
49+
for (const edge of edges) {
50+
const u = edge.startVertex.getKey();
51+
const v = edge.endVertex.getKey();
52+
const weight = edge.weight;
53+
if (dist[u] + weight < dist[v]) {
54+
throw new Error('Graph contains a negative-weight cycle');
55+
}
56+
}
57+
58+
return {
59+
dist,
60+
prev,
61+
};
62+
}

src/algorithms/graph/dijkstra/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ The algorithm exists in many variants. Dijkstra's original algorithm found the s
66

77
## Applications
88

9+
Same as the others algorithms (e.g. Bellman-Ford) for finding the shortest path:
10+
911
* Searching for a shortest path
1012
* Build an optimal path at geographical maps
1113
* Network routing protocols

src/data-structures/graph/graph.ts

+11-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,17 @@ export class Graph {
7676
* @memberof Graph
7777
*/
7878
getVertices(): GraphVertex[] {
79-
return Object.keys(this.verticies).map((k) => this.verticies[k]) || null;
79+
return Object.keys(this.verticies).map((k) => this.verticies[k]);
80+
}
81+
82+
/**
83+
*
84+
*
85+
* @return {GraphEdge[]}
86+
* @memberof Graph
87+
*/
88+
getEdges(): GraphEdge[] {
89+
return [].concat(...this.getVertices().map((v) => v.getEdges()));
8090
}
8191

8292
/**

src/data-structures/graph/graphVertex.ts

+8
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,12 @@ export class GraphVertex {
6464
return edge.startVertex === this ? edge.endVertex : edge.startVertex;
6565
});
6666
}
67+
68+
/**
69+
* @return {GraphEdge[]}
70+
* @memberof GraphVertex
71+
*/
72+
getEdges(): GraphEdge[] {
73+
return this.edges;
74+
}
6775
}

0 commit comments

Comments
 (0)