mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-11-14 06:52:59 +08:00
Add Bellman-Ford.
This commit is contained in:
parent
c97e472db7
commit
5788575718
@ -65,8 +65,8 @@
|
|||||||
* **Graph**
|
* **Graph**
|
||||||
* [Depth-First Search](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/depth-first-search) (DFS)
|
* [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)
|
* [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
|
* [Dijkstra Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/graph/dijkstra) - finding shortest path to all graph vertices
|
||||||
* Bellman Ford
|
* [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
|
* Detect Cycle
|
||||||
* Topological Sorting
|
* Topological Sorting
|
||||||
* Eulerian path, Eulerian circuit
|
* Eulerian path, Eulerian circuit
|
||||||
@ -84,7 +84,7 @@
|
|||||||
|
|
||||||
* **Greedy**
|
* **Greedy**
|
||||||
* [Unbound Knapsack Problem](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/sets/knapsack-problem)
|
* [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**
|
* **Divide and Conquer**
|
||||||
* [Euclidean Algorithm](https://github.com/trekhleb/javascript-algorithms/tree/master/src/algorithms/math/euclidean-algorithm) - calculate the Greatest Common Divisor (GCD)
|
* [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)
|
* [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)
|
* [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)
|
* [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)
|
* [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**
|
* **Backtracking**
|
||||||
* **Branch & Bound**
|
* **Branch & Bound**
|
||||||
|
|
||||||
|
21
src/algorithms/graph/bellman-ford/README.md
Normal file
21
src/algorithms/graph/bellman-ford/README.md
Normal file
@ -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)
|
@ -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
src/algorithms/graph/bellman-ford/bellmanFord.js
Normal file
45
src/algorithms/graph/bellman-ford/bellmanFord.js
Normal 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,
|
||||||
|
};
|
||||||
|
}
|
@ -61,5 +61,7 @@ describe('dijkstra', () => {
|
|||||||
expect(previousVertices.B.getKey()).toBe('A');
|
expect(previousVertices.B.getKey()).toBe('A');
|
||||||
expect(previousVertices.G.getKey()).toBe('E');
|
expect(previousVertices.G.getKey()).toBe('E');
|
||||||
expect(previousVertices.C.getKey()).toBe('A');
|
expect(previousVertices.C.getKey()).toBe('A');
|
||||||
|
expect(previousVertices.A).toBeNull();
|
||||||
|
expect(previousVertices.H).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -12,8 +12,9 @@ export default function dijkstra(graph, startVertex) {
|
|||||||
|
|
||||||
// Init all distances with infinity assuming that currently we can't reach
|
// Init all distances with infinity assuming that currently we can't reach
|
||||||
// any of the vertices except start one.
|
// any of the vertices except start one.
|
||||||
Object.keys(graph.vertices).forEach((vertexKey) => {
|
graph.getAllVertices().forEach((vertex) => {
|
||||||
distances[vertexKey] = Infinity;
|
distances[vertex.getKey()] = Infinity;
|
||||||
|
previousVertices[vertex.getKey()] = null;
|
||||||
});
|
});
|
||||||
distances[startVertex.getKey()] = 0;
|
distances[startVertex.getKey()] = 0;
|
||||||
|
|
||||||
|
@ -32,6 +32,13 @@ export default class Graph {
|
|||||||
return vertex.getNeighbors();
|
return vertex.getNeighbors();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {GraphVertex[]}
|
||||||
|
*/
|
||||||
|
getAllVertices() {
|
||||||
|
return Object.values(this.vertices);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {GraphEdge} edge
|
* @param {GraphEdge} edge
|
||||||
* @returns {Graph}
|
* @returns {Graph}
|
||||||
|
@ -28,6 +28,10 @@ describe('Graph', () => {
|
|||||||
|
|
||||||
graph.addEdge(edgeAB);
|
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 graphVertexA = graph.findVertexByKey(vertexA.getKey());
|
||||||
const graphVertexB = graph.findVertexByKey(vertexB.getKey());
|
const graphVertexB = graph.findVertexByKey(vertexB.getKey());
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user