Skip to content

Commit

Permalink
Add Bellman-Ford.
Browse files Browse the repository at this point in the history
  • Loading branch information
trekhleb committed May 3, 2018
1 parent c97e472 commit 5788575
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 5 deletions.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,8 @@
* **Graph**
* [Depth-First Search](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/depth-first-search) (DFS)
* [Breadth-First Search](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/breadth-first-search) (BFS)
* [Dijkstra Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/dijkstra) - finding shortest path
* Bellman Ford
* [Dijkstra Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/dijkstra) - finding shortest path to all graph vertices
* [Bellman-Ford Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/bellman-ford) - finding shortest path to all graph vertices
* Detect Cycle
* Topological Sorting
* Eulerian path, Eulerian circuit
Expand All @@ -84,7 +84,7 @@

* **Greedy**
* [Unbound Knapsack Problem](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/knapsack-problem)
* [Dijkstra Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/dijkstra) - finding shortest path
* [Dijkstra Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/dijkstra) - finding shortest path to all graph vertices
* **Divide and Conquer**
* [Euclidean Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/euclidean-algorithm) - calculate the Greatest Common Divisor (GCD)
* [Permutations](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/permutations) (with and without repetitions)
Expand All @@ -103,6 +103,7 @@
* [0/1 Knapsack Problem](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/knapsack-problem)
* [Integer Partition](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/integer-partition)
* [Maximum Subarray](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/maximum-subarray)
* [Bellman-Ford Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/bellman-ford) - finding shortest path to all graph vertices
* **Backtracking**
* **Branch & Bound**

Expand Down
21 changes: 21 additions & 0 deletions src/algorithms/graph/bellman-ford/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Bellman–Ford Algorithm

The Bellman–Ford algorithm is an algorithm that computes shortest
paths from a single source vertex to all of the other vertices
in a weighted digraph. It is slower than Dijkstra's algorithm
for the same problem, but more versatile, as it is capable of
handling graphs in which some of the edge weights are negative
numbers.

![Bellman-Ford](https://upload.wikimedia.org/wikipedia/commons/2/2e/Shortest_path_Dijkstra_vs_BellmanFord.gif)

## Complexity

Worst-case performance `O(|V||E|)`
Best-case performance `O(|E|)`
Worst-case space complexity `O(|V|)`

## References

- [Wikipedia](https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm)
- [On YouTube by Michael Sambol](https://www.youtube.com/watch?v=obWXjtg0L64)
56 changes: 56 additions & 0 deletions src/algorithms/graph/bellman-ford/__test__/bellmanFord.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import GraphVertex from '../../../../data-structures/graph/GraphVertex';
import GraphEdge from '../../../../data-structures/graph/GraphEdge';
import Graph from '../../../../data-structures/graph/Graph';
import bellmanFord from '../bellmanFord';

describe('bellmanFord', () => {
it('should find minimum paths to all vertices', () => {
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 } = bellmanFord(graph, vertexS);

expect(distances).toEqual({
H: Infinity,
S: 0,
A: 5,
B: 5,
C: 7,
D: 9,
E: 8,
});

expect(previousVertices.H).toBeNull();
expect(previousVertices.S).toBeNull();
expect(previousVertices.B.getKey()).toBe('C');
expect(previousVertices.C.getKey()).toBe('A');
expect(previousVertices.A.getKey()).toBe('D');
expect(previousVertices.D.getKey()).toBe('E');
});
});
45 changes: 45 additions & 0 deletions src/algorithms/graph/bellman-ford/bellmanFord.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* @param {Graph} graph
* @param {GraphVertex} startVertex
* @return {{distances, previousVertices}}
*/
export default function bellmanFord(graph, startVertex) {
const distances = {};
const previousVertices = {};

// Init all distances with infinity assuming that currently we can't reach
// any of the vertices except start one.
distances[startVertex.getKey()] = 0;
graph.getAllVertices().forEach((vertex) => {
previousVertices[vertex.getKey()] = null;
if (vertex.getKey() !== startVertex.getKey()) {
distances[vertex.getKey()] = Infinity;
}
});

// We need (|V| - 1) iterations.
for (let iteration = 0; iteration < (graph.getAllVertices().length - 1); iteration += 1) {
// During each iteration go through all vertices.
Object.keys(distances).forEach((vertexKey) => {
const vertex = graph.getVertexByKey(vertexKey);

// Go through all vertex edges.
graph.getNeighbors(vertex).forEach((neighbor) => {
const edge = graph.findEdge(vertex, neighbor);
// Find out if the distance to the neighbor is shorter in this iteration
// then in previous one.
const distanceToVertex = distances[vertex.getKey()];
const distanceToNeighbor = distanceToVertex + edge.weight;
if (distanceToNeighbor < distances[neighbor.getKey()]) {
distances[neighbor.getKey()] = distanceToNeighbor;
previousVertices[neighbor.getKey()] = vertex;
}
});
});
}

return {
distances,
previousVertices,
};
}
2 changes: 2 additions & 0 deletions src/algorithms/graph/dijkstra/__test__/dijkstra.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,7 @@ describe('dijkstra', () => {
expect(previousVertices.B.getKey()).toBe('A');
expect(previousVertices.G.getKey()).toBe('E');
expect(previousVertices.C.getKey()).toBe('A');
expect(previousVertices.A).toBeNull();
expect(previousVertices.H).toBeNull();
});
});
5 changes: 3 additions & 2 deletions src/algorithms/graph/dijkstra/dijkstra.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ export default function dijkstra(graph, startVertex) {

// Init all distances with infinity assuming that currently we can't reach
// any of the vertices except start one.
Object.keys(graph.vertices).forEach((vertexKey) => {
distances[vertexKey] = Infinity;
graph.getAllVertices().forEach((vertex) => {
distances[vertex.getKey()] = Infinity;
previousVertices[vertex.getKey()] = null;
});
distances[startVertex.getKey()] = 0;

Expand Down
7 changes: 7 additions & 0 deletions src/data-structures/graph/Graph.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ export default class Graph {
return vertex.getNeighbors();
}

/**
* @return {GraphVertex[]}
*/
getAllVertices() {
return Object.values(this.vertices);
}

/**
* @param {GraphEdge} edge
* @returns {Graph}
Expand Down
4 changes: 4 additions & 0 deletions src/data-structures/graph/__test__/Graph.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ describe('Graph', () => {

graph.addEdge(edgeAB);

expect(graph.getAllVertices().length).toBe(2);
expect(graph.getAllVertices()[0]).toEqual(vertexA);
expect(graph.getAllVertices()[1]).toEqual(vertexB);

const graphVertexA = graph.findVertexByKey(vertexA.getKey());
const graphVertexB = graph.findVertexByKey(vertexB.getKey());

Expand Down

0 comments on commit 5788575

Please sign in to comment.