Skip to content

Commit 3d329d4

Browse files
committed
Add Ford-Fulkerson
1 parent c9758a4 commit 3d329d4

File tree

5 files changed

+298
-3
lines changed

5 files changed

+298
-3
lines changed

data-structures/graph/graph.js

Lines changed: 126 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,101 @@ class Graph {
147147
}
148148
return false;
149149
};
150+
// For Ford-Fulkerson
151+
// Returns boolean for an existing augmenting path, and the path between s -> t
152+
this.augmentingPath = (s, t, g) => {
153+
const { dist, parents } = g.bfs(s);
154+
const exists = dist.get(t) !== undefined ? true : false;
155+
if (!exists)
156+
return { exists: false, path: undefined };
157+
const path = this.getPath(s, t, parents);
158+
return { exists, path };
159+
};
160+
this.getPath = (s, t, parents) => {
161+
// go back from t until we reach s
162+
const path = [];
163+
let a = parents.get(t);
164+
path.push(t);
165+
while (a !== s) {
166+
path.push(a);
167+
a = parents.get(a);
168+
}
169+
path.push(s);
170+
return path.reverse();
171+
};
172+
// Returns the smallest capacity that remains in a path between s - t in a Net-Flow
173+
this.bottleneck = (path, g) => {
174+
let min = Infinity;
175+
// for each edge(u,v) of this path check the capacity that remains
176+
for (let i = 0; i < path.length - 1; i++) {
177+
const u = path[i];
178+
const v = path[i + 1];
179+
const list = g.list.get(u);
180+
// search for v in adj list of u
181+
for (let node of list) {
182+
if (v === node.node) {
183+
// check if edge(u,v) < min
184+
if (min > node.weight) {
185+
min = node.weight;
186+
}
187+
}
188+
}
189+
}
190+
return min;
191+
};
192+
this.createResidualGraph = () => {
193+
const rg = new Graph(true);
194+
for (let [u, vertexList] of this.list) {
195+
for (let v in vertexList) {
196+
// original edge = c(u,v) - f(u,v)
197+
rg.addVertecesAndEdge(u, vertexList[v].node, vertexList[v].weight);
198+
rg.addVertecesAndEdge(vertexList[v].node, u, 0);
199+
}
200+
}
201+
return rg;
202+
};
203+
this.updateResidualGraph = (g, path, bottleneck) => {
204+
// for each edge of this path
205+
for (let i = 0; i < path.length - 1; i++) {
206+
// we need to check if edge(u,v) is from the original Graph or from the Residual Graph
207+
// let isResidual = true;
208+
const u = path[i];
209+
const v = path[i + 1];
210+
const listU = g.list.get(u);
211+
const listV = g.list.get(v);
212+
let updatedEdgeUV;
213+
let updatedEdgeVU;
214+
// search for edge(u,v) in residual graph
215+
for (let i = 0; i < listU.length; i++) {
216+
const t = listU[i];
217+
if (v == t.node) {
218+
updatedEdgeUV = {
219+
node: t.node,
220+
weight: t.weight - bottleneck,
221+
};
222+
const updatedList = [...listU];
223+
updatedList[i] = updatedEdgeUV;
224+
g.list.set(u, updatedList);
225+
break;
226+
}
227+
}
228+
// search for edge(v,v) in residual graph
229+
for (let i = 0; i < listV.length; i++) {
230+
const t = listV[i];
231+
if (u == t.node) {
232+
updatedEdgeVU = {
233+
node: t.node,
234+
weight: t.weight + bottleneck,
235+
};
236+
const updatedList = [...listV];
237+
updatedList[i] = updatedEdgeVU;
238+
g.list.set(v, updatedList);
239+
break;
240+
}
241+
}
242+
}
243+
return g;
244+
};
150245
// **********************************************************
151246
// INSERT
152247
// **********************************************************
@@ -301,7 +396,7 @@ class Graph {
301396
result.push(v.key);
302397
this.list.get(v.key).forEach((u) => {
303398
// if u unvisited
304-
if (!visited.get(u.node)) {
399+
if (!visited.get(u.node) && u.weight > 0) {
305400
// mark u as visited
306401
visited.set(u.node, true);
307402
// add u to the queue
@@ -744,6 +839,36 @@ class Graph {
744839
}
745840
return { cost, MST };
746841
};
842+
// Returns the final Residual Graph
843+
this.fordFulkerson = (s, t) => {
844+
if (!this.directed)
845+
return false;
846+
const flow = new Map();
847+
// Initially f(e)=0 for all e in G
848+
// for each edge of G f(u,v) = 0
849+
for (let [u, vertexList] of this.list) {
850+
flow.set(u, new Map());
851+
for (let v of vertexList) {
852+
flow.get(u).set(v.node, 0);
853+
}
854+
}
855+
let residualGraph = this.createResidualGraph();
856+
residualGraph.print();
857+
let { exists, path } = this.augmentingPath(s, t, residualGraph);
858+
// if there is no path between s - t return false
859+
if (!exists || !path)
860+
return false;
861+
const bottleneck = this.bottleneck(path, residualGraph);
862+
// while exists a path do augmenting path in Gr
863+
while (exists) {
864+
const bottleneck = this.bottleneck(path, residualGraph);
865+
residualGraph = this.updateResidualGraph(residualGraph, path, bottleneck);
866+
const ap = this.augmentingPath(s, t, residualGraph);
867+
exists = ap.exists;
868+
path = ap.path;
869+
}
870+
return residualGraph;
871+
};
747872
this.list = new Map();
748873
this.currentLabel = null;
749874
this.size = 0;

data-structures/graph/graph.ts

Lines changed: 136 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,106 @@ class Graph<T> {
161161
return false;
162162
};
163163

164+
// For Ford-Fulkerson
165+
// Returns boolean for an existing augmenting path, and the path between s -> t
166+
augmentingPath = (s: T, t: T, g: Graph<T>) => {
167+
const { dist, parents } = g.bfs(s);
168+
const exists = dist.get(t) !== undefined ? true : false;
169+
if (!exists) return { exists: false, path: undefined };
170+
const path = this.getPath(s, t, parents);
171+
return { exists, path };
172+
};
173+
174+
getPath = (s: T, t: T, parents: Map<T, T | null>) => {
175+
// go back from t until we reach s
176+
const path: Array<T> = [];
177+
let a = parents.get(t)!;
178+
path.push(t);
179+
while (a !== s) {
180+
path.push(a);
181+
a = parents.get(a)!;
182+
}
183+
path.push(s);
184+
return path.reverse();
185+
};
186+
187+
// Returns the smallest capacity that remains in a path between s - t in a Net-Flow
188+
bottleneck = (path: Array<T>, g: Graph<T>) => {
189+
let min = Infinity;
190+
// for each edge(u,v) of this path check the capacity that remains
191+
for (let i = 0; i < path.length - 1; i++) {
192+
const u = path[i];
193+
const v = path[i + 1];
194+
const list = g.list.get(u)!;
195+
// search for v in adj list of u
196+
for (let node of list) {
197+
if (v === node.node) {
198+
// check if edge(u,v) < min
199+
if (min > node.weight) {
200+
min = node.weight;
201+
}
202+
}
203+
}
204+
}
205+
return min;
206+
};
207+
208+
createResidualGraph = () => {
209+
const rg = new Graph<T>(true);
210+
for (let [u, vertexList] of this.list) {
211+
for (let v in vertexList) {
212+
// original edge = c(u,v)
213+
rg.addVertecesAndEdge(u, vertexList[v].node, vertexList[v].weight);
214+
// residual edges
215+
rg.addVertecesAndEdge(vertexList[v].node, u, 0);
216+
}
217+
}
218+
return rg;
219+
};
220+
221+
updateResidualGraph = (g: Graph<T>, path: Array<T>, bottleneck: number) => {
222+
// for each edge of this path
223+
for (let i = 0; i < path.length - 1; i++) {
224+
// we need to check if edge(u,v) is from the original Graph or from the Residual Graph
225+
// let isResidual = true;
226+
const u = path[i];
227+
const v = path[i + 1];
228+
const listU = g.list.get(u)!;
229+
const listV = g.list.get(v)!;
230+
let updatedEdgeUV: Vertex<T>;
231+
let updatedEdgeVU: Vertex<T>;
232+
// search for edge(u,v) in residual graph
233+
for (let i = 0; i < listU.length; i++) {
234+
const t = listU[i];
235+
if (v == t.node) {
236+
updatedEdgeUV = {
237+
node: t.node,
238+
weight: t.weight - bottleneck,
239+
};
240+
const updatedList = [...listU];
241+
updatedList[i] = updatedEdgeUV;
242+
g.list.set(u, updatedList);
243+
break;
244+
}
245+
}
246+
// search for edge(v,v) in residual graph
247+
for (let i = 0; i < listV.length; i++) {
248+
const t = listV[i];
249+
if (u == t.node) {
250+
updatedEdgeVU = {
251+
node: t.node,
252+
weight: t.weight + bottleneck,
253+
};
254+
const updatedList = [...listV];
255+
updatedList[i] = updatedEdgeVU;
256+
g.list.set(v, updatedList);
257+
break;
258+
}
259+
}
260+
}
261+
return g;
262+
};
263+
164264
// **********************************************************
165265
// INSERT
166266
// **********************************************************
@@ -456,7 +556,7 @@ class Graph<T> {
456556
// Maps the key of each vertex to its distance from vertex s
457557
const dist: Map<T, number> = new Map();
458558
// Maps the key of each vertex to its parents key
459-
const parents: Map<T, null | string> = new Map();
559+
const parents: Map<T, null | T> = new Map();
460560
const visited: Map<T, boolean> = new Map();
461561
const q: Queue = new Queue();
462562
// add s to the queue
@@ -471,7 +571,7 @@ class Graph<T> {
471571
result.push(v.key);
472572
this.list.get(v.key)!.forEach((u) => {
473573
// if u unvisited
474-
if (!visited.get(u.node)) {
574+
if (!visited.get(u.node) && u.weight > 0) {
475575
// mark u as visited
476576
visited.set(u.node, true);
477577
// add u to the queue
@@ -949,6 +1049,40 @@ class Graph<T> {
9491049
}
9501050
return { cost, MST };
9511051
};
1052+
1053+
// Returns the final Residual Graph
1054+
fordFulkerson = (s: T, t: T) => {
1055+
if (!this.directed) return false;
1056+
const flow = new Map<T, Map<T, number>>();
1057+
// Initially f(e)=0 for all e in G
1058+
// for each edge of G f(u,v) = 0
1059+
for (let [u, vertexList] of this.list) {
1060+
flow.set(u, new Map<T, number>());
1061+
for (let v of vertexList) {
1062+
flow.get(u)!.set(v.node, 0);
1063+
}
1064+
}
1065+
let residualGraph: Graph<T> = this.createResidualGraph();
1066+
residualGraph.print();
1067+
let { exists, path } = this.augmentingPath(s, t, residualGraph);
1068+
// if there is no path between s - t return false
1069+
if (!exists || !path) return false;
1070+
1071+
const bottleneck = this.bottleneck(path, residualGraph);
1072+
// while exists a path do augmenting path in Gr
1073+
while (exists) {
1074+
const bottleneck = this.bottleneck(path!, residualGraph);
1075+
residualGraph = this.updateResidualGraph(
1076+
residualGraph,
1077+
path!,
1078+
bottleneck
1079+
);
1080+
const ap = this.augmentingPath(s, t, residualGraph);
1081+
exists = ap.exists;
1082+
path = ap.path;
1083+
}
1084+
return residualGraph;
1085+
};
9521086
}
9531087

9541088
export = Graph;

greedy/ford-fulkerson/exe.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"use strict";
2+
var __importDefault = (this && this.__importDefault) || function (mod) {
3+
return (mod && mod.__esModule) ? mod : { "default": mod };
4+
};
5+
Object.defineProperty(exports, "__esModule", { value: true });
6+
const graph_1 = __importDefault(require("../../data-structures/graph/graph"));
7+
const test = (file) => {
8+
const g = graph_1.default.createDirected(file, true, false);
9+
const rg = g.fordFulkerson("s", "t");
10+
if (rg) {
11+
// console.log("LALALALALALALLA");
12+
// rg.print();
13+
}
14+
};
15+
test("test.txt");

greedy/ford-fulkerson/exe.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import Graph from "../../data-structures/graph/graph";
2+
3+
const test = (file: string) => {
4+
const g = Graph.createDirected(file, true, false);
5+
const rg = g.fordFulkerson("s", "t");
6+
if (rg) {
7+
// console.log("LALALALALALALLA");
8+
// rg.print();
9+
}
10+
};
11+
12+
test("test.txt");

greedy/ford-fulkerson/test.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
s u 3
2+
s x 2
3+
x u 3
4+
u v 2
5+
v x 1
6+
x y 3
7+
y v 3
8+
y t 2
9+
v t 3

0 commit comments

Comments
 (0)