Add Bellman-Ford.

This commit is contained in:
Oleksii Trekhleb 2018-05-03 09:58:00 +03:00
parent c97e472db7
commit 5788575718
8 changed files with 142 additions and 5 deletions

View File

@ -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
@ -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)
@ -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**

View File

@ -0,0 +1,21 @@
# BellmanFord Algorithm
The BellmanFord 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)

View File

@ -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');
});
});

View File

@ -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,
};
}

View File

@ -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();
});
});

View File

@ -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;

View File

@ -32,6 +32,13 @@ export default class Graph {
return vertex.getNeighbors();
}
/**
* @return {GraphVertex[]}
*/
getAllVertices() {
return Object.values(this.vertices);
}
/**
* @param {GraphEdge} edge
* @returns {Graph}

View File

@ -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());