Skip to content

Commit 19b4ced

Browse files
Added the implementation of the Edmond Karp algorithm along with test cases (#252)
* Added the implementation for bijection method binary to decimal and euler method * Added the implementation of the edmondkarp along with tests * Update graph/edmondkarp.ts Co-authored-by: Lars Müller <[email protected]> * Update edmondkarp.ts * Update graph/edmondkarp.ts Co-authored-by: Lars Müller <[email protected]> * Implement the furhter changes to make the algorithm more refined * Updated the test cases Updated the test cases * Updated the code and rewrite some functions Updated the code and rewrite some functions * Implement the optimal stack queue implementation in the edmond karp algorithm * removed the bisection_method.ts, decimal_convert.ts, euler_method.ts * Revert changes to package.json and package-lock.json --------- Co-authored-by: Lars Müller <[email protected]>
1 parent 44127b2 commit 19b4ced

File tree

2 files changed

+179
-0
lines changed

2 files changed

+179
-0
lines changed

graph/edmonds_karp.ts

+97
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { StackQueue } from '../data_structures/queue/stack_queue'
2+
3+
/**
4+
* @function edmondsKarp
5+
* @description Compute the maximum flow from a source node to a sink node using the Edmonds-Karp algorithm.
6+
* @Complexity_Analysis
7+
* Time complexity: O(V * E^2) where V is the number of vertices and E is the number of edges.
8+
* Space Complexity: O(E) due to residual graph representation.
9+
* @param {[number, number][][]} graph - The graph in adjacency list form.
10+
* @param {number} source - The source node.
11+
* @param {number} sink - The sink node.
12+
* @return {number} - The maximum flow from the source node to the sink node.
13+
* @see https://en.wikipedia.org/wiki/Edmonds%E2%80%93Karp_algorithm
14+
*/
15+
export default function edmondsKarp(
16+
graph: [number, number][][],
17+
source: number,
18+
sink: number
19+
): number {
20+
const n = graph.length
21+
22+
// Initialize residual graph
23+
const residualGraph: [number, number][][] = Array.from(
24+
{ length: n },
25+
() => []
26+
)
27+
28+
// Build residual graph from the original graph
29+
for (let u = 0; u < n; u++) {
30+
for (const [v, cap] of graph[u]) {
31+
if (cap > 0) {
32+
residualGraph[u].push([v, cap]) // Forward edge
33+
residualGraph[v].push([u, 0]) // Reverse edge with 0 capacity
34+
}
35+
}
36+
}
37+
38+
const findAugmentingPath = (parent: (number | null)[]): number => {
39+
const visited = Array(n).fill(false)
40+
const queue = new StackQueue<number>()
41+
queue.enqueue(source)
42+
visited[source] = true
43+
parent[source] = null
44+
45+
while (queue.length() > 0) {
46+
const u = queue.dequeue()
47+
for (const [v, cap] of residualGraph[u]) {
48+
if (!visited[v] && cap > 0) {
49+
parent[v] = u
50+
visited[v] = true
51+
if (v === sink) {
52+
// Return the bottleneck capacity along the path
53+
let pathFlow = Infinity
54+
let current = v
55+
while (parent[current] !== null) {
56+
const prev = parent[current]!
57+
const edgeCap = residualGraph[prev].find(
58+
([node]) => node === current
59+
)![1]
60+
pathFlow = Math.min(pathFlow, edgeCap)
61+
current = prev
62+
}
63+
return pathFlow
64+
}
65+
queue.enqueue(v)
66+
}
67+
}
68+
}
69+
return 0
70+
}
71+
72+
let maxFlow = 0
73+
const parent = Array(n).fill(null)
74+
75+
while (true) {
76+
const pathFlow = findAugmentingPath(parent)
77+
if (pathFlow === 0) break // No augmenting path found
78+
79+
// Update the capacities and reverse capacities in the residual graph
80+
let v = sink
81+
while (parent[v] !== null) {
82+
const u = parent[v]!
83+
// Update capacity of the forward edge
84+
const forwardEdge = residualGraph[u].find(([node]) => node === v)!
85+
forwardEdge[1] -= pathFlow
86+
// Update capacity of the reverse edge
87+
const reverseEdge = residualGraph[v].find(([node]) => node === u)!
88+
reverseEdge[1] += pathFlow
89+
90+
v = u
91+
}
92+
93+
maxFlow += pathFlow
94+
}
95+
96+
return maxFlow
97+
}

graph/test/edmonds_karp.test.ts

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import edmondsKarp from '../edmonds_karp'
2+
3+
describe('Edmonds-Karp Algorithm', () => {
4+
it('should find the maximum flow in a simple graph', () => {
5+
const graph: [number, number][][] = [
6+
[
7+
[1, 3],
8+
[2, 2]
9+
], // Node 0: Edges to node 1 (capacity 3), and node 2 (capacity 2)
10+
[[3, 2]], // Node 1: Edge to node 3 (capacity 2)
11+
[[3, 3]], // Node 2: Edge to node 3 (capacity 3)
12+
[] // Node 3: No outgoing edges
13+
]
14+
const source = 0
15+
const sink = 3
16+
const maxFlow = edmondsKarp(graph, source, sink)
17+
expect(maxFlow).toBe(4)
18+
})
19+
20+
it('should find the maximum flow in a more complex graph', () => {
21+
const graph: [number, number][][] = [
22+
[
23+
[1, 10],
24+
[2, 10]
25+
], // Node 0: Edges to node 1 and node 2 (both capacity 10)
26+
[
27+
[3, 4],
28+
[4, 8]
29+
], // Node 1: Edges to node 3 (capacity 4), and node 4 (capacity 8)
30+
[[4, 9]], // Node 2: Edge to node 4 (capacity 9)
31+
[[5, 10]], // Node 3: Edge to node 5 (capacity 10)
32+
[[5, 10]], // Node 4: Edge to node 5 (capacity 10)
33+
[] // Node 5: No outgoing edges (sink)
34+
]
35+
const source = 0
36+
const sink = 5
37+
const maxFlow = edmondsKarp(graph, source, sink)
38+
expect(maxFlow).toBe(14)
39+
})
40+
41+
it('should return 0 when there is no path from source to sink', () => {
42+
const graph: [number, number][][] = [
43+
[], // Node 0: No outgoing edges
44+
[], // Node 1: No outgoing edges
45+
[] // Node 2: No outgoing edges (sink)
46+
]
47+
const source = 0
48+
const sink = 2
49+
const maxFlow = edmondsKarp(graph, source, sink)
50+
expect(maxFlow).toBe(0)
51+
})
52+
53+
it('should handle graphs with no edges', () => {
54+
const graph: [number, number][][] = [
55+
[], // Node 0: No outgoing edges
56+
[], // Node 1: No outgoing edges
57+
[] // Node 2: No outgoing edges
58+
]
59+
const source = 0
60+
const sink = 2
61+
const maxFlow = edmondsKarp(graph, source, sink)
62+
expect(maxFlow).toBe(0)
63+
})
64+
65+
it('should handle graphs with self-loops', () => {
66+
const graph: [number, number][][] = [
67+
[
68+
[0, 10],
69+
[1, 10]
70+
], // Node 0: Self-loop with capacity 10, and edge to node 1 (capacity 10)
71+
[
72+
[1, 10],
73+
[2, 10]
74+
], // Node 1: Self-loop and edge to node 2
75+
[] // Node 2: No outgoing edges (sink)
76+
]
77+
const source = 0
78+
const sink = 2
79+
const maxFlow = edmondsKarp(graph, source, sink)
80+
expect(maxFlow).toBe(10)
81+
})
82+
})

0 commit comments

Comments
 (0)