-
Notifications
You must be signed in to change notification settings - Fork 311
/
bellmanFord.ts
142 lines (130 loc) · 5.03 KB
/
bellmanFord.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
type Vertex = string;
type Weight = number;
type Graph = Record<string, Record<string, Weight>>;
/**
* If a target is not provided, finds the single-source shortest path distance from the source vertex with positive or negative edge weights
* (but with no negative weight cycles).
* Else, it returns the shortest path as well as its distance from source vertex to target vertex.
* In the case of negative weight cycles, if target is defined, then negative infinity and empty array path will be returned,
* else, all distances from A will be returned with negative infinity.
*
* @param {Graph} graph Vertex with its fields being the neighboring vertices and value being the weight from vertex to neighboring vertices
* @param {Vertex} source Starting vertex in graph
* @param {Vertex} target Ending vertex in graph
* @return {Record<string, number> | [number, Array<Vertex>]} An object mapping of the distance from the source vertex or an array containing the distance
* and an array containing the path from source to target vertex
*/
function bellmanFord(
graph: Graph,
source: Vertex,
target?: Vertex,
): Record<string, number> | [number, Array<Vertex>] {
const vertices = Object.keys(graph);
// if graph is empty, immediately return
if (vertices.length === 0) {
return target ? [0, []] : {};
}
if (!vertices.includes(source) || (target && !vertices.includes(target))) {
return target ? [-Infinity, []] : {};
}
const distances = getDistances(vertices, source);
const predecessor = getPredecessor(vertices);
// requires only n - 1 iteration
for (let i = 0; i < vertices.length - 1; i++) {
for (let startVertex of vertices) {
const neighbors = graph[startVertex];
for (let endVertex in neighbors) {
const weight = graph[startVertex][endVertex];
// relaxation
if (distances[startVertex] + weight < distances[endVertex]) {
distances[endVertex] = distances[startVertex] + weight;
predecessor[endVertex] = startVertex;
}
}
}
}
// checks for any negative weight cycles. If true returns all distances as -Infinity as a sign negative weight cycles
for (let startVertex of vertices) {
const neighbors = graph[startVertex];
for (let endVertex in neighbors) {
const weight = graph[startVertex][endVertex];
// if relaxation occurs here, negative weight cycles detected
if (distances[startVertex] + weight < distances[endVertex]) {
return target ? [-Infinity, []] : negativeWeightCycles(vertices);
}
}
}
if (!target) {
return distances;
}
const shortestDistance = distances[target];
const path = getPath(predecessor, source, target);
return [shortestDistance, path];
}
/**
* Returns an Object mapping of nodes to distance with values all being negative infinity to represent a negative weight cycle being present.
*
* @param {Array<Vertex>} vertices An array of all vertices in the graph
* @return {Record<string, number>} An object mapping of nodes with values being negative infinity
*/
function negativeWeightCycles(vertices: Array<Vertex>): Record<string, number> {
return vertices.reduce((acc, curr) => {
acc[curr] = -Infinity;
return acc;
}, {} as Record<string, number>);
}
/**
* Initializes the distance mapping from source to rest of the vertices.
*
* @param {Array<Vertex>} vertices An array of all vertices in the graph
* @param {Vertex} source Starting vertex in graph
* @return {Record<string, number>} An object mapping of source to rest of vertices
*/
function getDistances(
vertices: Array<Vertex>,
source: Vertex,
): Record<string, number> {
const distances = vertices.reduce((acc, curr) => {
acc[curr] = Infinity;
return acc;
}, {} as Record<string, number>);
distances[source] = 0;
return distances;
}
/**
* Returns an array denoting the path from start vertex to end vertex.
*
* @param {Object} predecessor Mapping of child vertex pointing to parent vertex
* @param {Vertex} source Starting vertex in graph
* @param {Vertex} target End vertex in graph
* @return {Array<String>} An array containing the path from source to target vertex
*/
function getPath(
predecessor: Record<string, string | undefined>,
source: Vertex,
target: Vertex,
): Array<string> {
const path = [target];
while (target && target !== source) {
const next = predecessor[target];
target = next ?? source; // if next is undefined, it reaches source
path.push(target);
}
return path.reverse();
}
/**
* Generates a default object mapping of child vertex pointing to parent vertex which will be defaulted to undefined initially.
*
* @param {Array<Vertex>} vertices An array of all vertices in the graph
* @return {Record<string, Vertex | undefined>} An object mapping of child vertex pointing to parent vertex
*/
function getPredecessor(
vertices: Array<Vertex>,
): Record<string, Vertex | undefined> {
const predecessor = {} as Record<string, Vertex | undefined>;
for (let vertex of vertices) {
predecessor[vertex] = undefined;
}
return predecessor;
}
export default bellmanFord;