Skip to content

Commit 13ba5a3

Browse files
Update edmondkarp.ts
1 parent b560c2e commit 13ba5a3

File tree

1 file changed

+64
-42
lines changed

1 file changed

+64
-42
lines changed

graph/edmondkarp.ts

Lines changed: 64 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,97 @@
11
/**
2-
* @description Compute the maximum flow from a source node to a sink node. The input graph is in adjacency list form. It is a multidimensional array of edges. graph[i] holds the edges for the i'th node. Each edge is a 3-tuple where the 0'th item is the destination node, the 1'th item is the edge weight, and the 2'nd item is the edge capacity.
2+
* @function edmondkarp
3+
* @description Compute the maximum flow from a source node to a sink node. The input graph is in adjacency list form. It is a multidimensional array of edges. graph[i] holds the edges for the i'th node. Each edge is a 2-tuple where the 0'th item is the destination node, and the 1'st item is the edge capacity.
34
* @Complexity_Analysis
4-
* Time complexity: O(V*E^2) where V is the number of vertices and E is the number of edges
5-
* Space Complexity: O(V) where V is the number of vertices
6-
* @param {[number, number, number][][]} graph - The graph in adjacency list form
5+
* Time complexity: O(V * E^2) where V is the number of vertices and E is the number of edges
6+
* Space Complexity: O(V^2) where V is the number of vertices
7+
* @param {[number, number][][]} graph - The graph in adjacency list form
78
* @param {number} source - The source node
89
* @param {number} sink - The sink node
910
* @return {number} - The maximum flow from the source node to the sink node
1011
* @see https://en.wikipedia.org/wiki/Edmonds%E2%80%93Karp_algorithm
1112
*/
1213

13-
function edmondkarp(graph: [number, number, number][][], source: number, sink: number): number {
14-
// Initialize capacity and flow matrices with zeros and build capacity matrix from graph
14+
function edmondkarp(graph: [number, number][][], source: number, sink: number): number {
1515
const n = graph.length;
16-
const capacity = Array.from({ length: n }, () => Array(n).fill(0));
17-
const flow = Array.from({ length: n }, () => Array(n).fill(0));
1816

19-
// Build capacity matrix
17+
// Residual graph in adjacency list form
18+
const residualGraph: Map<number, [number, number][]> = new Map();
2019
for (let u = 0; u < n; u++) {
21-
for (const [v, , cap] of graph[u]) {
22-
capacity[u][v] = cap;
20+
residualGraph.set(u, []);
21+
for (const [v, capacity] of graph[u]) {
22+
residualGraph.get(u)?.push([v, capacity]);
23+
if (!residualGraph.has(v)) {
24+
residualGraph.set(v, []);
25+
}
2326
}
2427
}
2528

26-
// Breadth-first search
27-
const bfs = (parent: number[]): boolean => {
29+
const parent = Array(n).fill(null);
30+
let maxFlow = 0;
31+
32+
// Level-order BFS using two level arrays
33+
const bfs = (): boolean => {
2834
const visited = Array(n).fill(false);
29-
const queue: number[] = [];
30-
queue.push(source);
35+
const currentLevel: number[] = [];
36+
const nextLevel: number[] = [];
37+
currentLevel.push(source);
3138
visited[source] = true;
3239

33-
// Find an augmenting path from source to sink by doing a BFS traversal
34-
while (queue.length > 0) {
35-
// Dequeue
36-
const u = queue.shift()!;
37-
// Enqueue all adjacent unvisited vertices with available capacity
38-
for (let v = 0; v < n; v++) {
39-
// If there is available capacity and the vertex has not been visited
40-
if (!visited[v] && capacity[u][v] - flow[u][v] > 0) {
41-
queue.push(v);
42-
visited[v] = true;
43-
parent[v] = u;
44-
// If we reach the sink, we have found the augmenting path
45-
if (v === sink) {
46-
return true;
40+
while (currentLevel.length > 0) {
41+
nextLevel.length = 0;
42+
43+
for (const u of currentLevel) {
44+
for (const [v, capacity] of residualGraph.get(u)!) {
45+
if (!visited[v] && capacity > 0) {
46+
parent[v] = u;
47+
visited[v] = true;
48+
49+
// If we reach the sink, we have found an augmenting path
50+
if (v === sink) {
51+
return true;
52+
}
53+
54+
nextLevel.push(v);
4755
}
4856
}
4957
}
58+
59+
currentLevel.length = 0;
60+
currentLevel.push(...nextLevel);
5061
}
62+
5163
return false;
5264
};
5365

54-
let maxFlow = 0;
55-
const parent = Array(n).fill(-1);
56-
57-
while (bfs(parent)) {
66+
while (bfs()) {
5867
let pathFlow = Infinity;
68+
5969
// Find the maximum flow through the path found
60-
for (let v = sink; v !== source; v = parent[v]) {
61-
const u = parent[v];
62-
pathFlow = Math.min(pathFlow, capacity[u][v] - flow[u][v]);
70+
for (let v = sink; v !== source; v = parent[v]!) {
71+
const u = parent[v]!;
72+
const edge = residualGraph.get(u)!.find(([dest]) => dest === v)!;
73+
pathFlow = Math.min(pathFlow, edge[1]);
6374
}
64-
// Update the flow matrix
65-
for (let v = sink; v !== source; v = parent[v]) {
66-
const u = parent[v];
67-
flow[u][v] += pathFlow;
68-
flow[v][u] -= pathFlow;
75+
76+
// Update the residual graph
77+
for (let v = sink; v !== source; v = parent[v]!) {
78+
const u = parent[v]!;
79+
80+
// Update forward edge
81+
const edgeIndex = residualGraph.get(u)!.findIndex(([dest]) => dest === v);
82+
residualGraph.get(u)![edgeIndex][1] -= pathFlow;
83+
84+
// Update backward edge
85+
const reverseEdge = residualGraph.get(v)!.find(([dest]) => dest === u);
86+
if (reverseEdge) {
87+
reverseEdge[1] += pathFlow;
88+
} else {
89+
residualGraph.get(v)!.push([u, pathFlow]);
90+
}
6991
}
7092

7193
maxFlow += pathFlow;
7294
}
7395

7496
return maxFlow;
75-
}
97+
}

0 commit comments

Comments
 (0)