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 <34514239+appgurueu@users.noreply.github.com> * Update edmondkarp.ts * Update graph/edmondkarp.ts Co-authored-by: Lars Müller <34514239+appgurueu@users.noreply.github.com> * 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 <34514239+appgurueu@users.noreply.github.com>
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

Lines changed: 97 additions & 0 deletions
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

Lines changed: 82 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy