-
-
Notifications
You must be signed in to change notification settings - Fork 312
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added the implementation of the edmondkarp along with tests
- Loading branch information
1 parent
9b477f1
commit a358ea6
Showing
2 changed files
with
160 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
/** | ||
* @function edmondkarp | ||
* @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. | ||
* @Complexity_Analysis | ||
* Time complexity: O(V*E^2) where V is the number of vertices and E is the number of edges | ||
* Space Complexity: O(V) where V is the number of vertices | ||
* @param {[number, number, number][][]} graph - The graph in adjacency list form | ||
* @param {number} source - The source node | ||
* @param {number} sink - The sink node | ||
* @return {number} - The maximum flow from the source node to the sink node | ||
* @see https://en.wikipedia.org/wiki/Edmonds%E2%80%93Karp_algorithm | ||
*/ | ||
|
||
function edmondkarp(graph: [number, number, number][][], source: number, sink: number): number { | ||
// Initialize capacity and flow matrices with zeros and build capacity matrix from graph | ||
const n = graph.length; | ||
const capacity = Array.from({ length: n }, () => Array(n).fill(0)); | ||
const flow = Array.from({ length: n }, () => Array(n).fill(0)); | ||
|
||
// Build capacity matrix | ||
for (let u = 0; u < n; u++) { | ||
for (const [v, , cap] of graph[u]) { | ||
capacity[u][v] = cap; | ||
} | ||
} | ||
|
||
// Breadth-first search | ||
const bfs = (parent: number[]): boolean => { | ||
const visited = Array(n).fill(false); | ||
const queue: number[] = []; | ||
queue.push(source); | ||
visited[source] = true; | ||
|
||
// Find an augmenting path from source to sink by doing a BFS traversal | ||
while (queue.length > 0) { | ||
// Dequeue | ||
const u = queue.shift()!; | ||
// Enqueue all adjacent unvisited vertices with available capacity | ||
for (let v = 0; v < n; v++) { | ||
// If there is available capacity and the vertex has not been visited | ||
if (!visited[v] && capacity[u][v] - flow[u][v] > 0) { | ||
queue.push(v); | ||
visited[v] = true; | ||
parent[v] = u; | ||
// If we reach the sink, we have found the augmenting path | ||
if (v === sink) { | ||
return true; | ||
} | ||
} | ||
} | ||
} | ||
return false; | ||
}; | ||
|
||
let maxFlow = 0; | ||
const parent = Array(n).fill(-1); | ||
|
||
while (bfs(parent)) { | ||
let pathFlow = Infinity; | ||
// Find the maximum flow through the path found | ||
for (let v = sink; v !== source; v = parent[v]) { | ||
const u = parent[v]; | ||
pathFlow = Math.min(pathFlow, capacity[u][v] - flow[u][v]); | ||
} | ||
// Update the flow matrix | ||
for (let v = sink; v !== source; v = parent[v]) { | ||
const u = parent[v]; | ||
flow[u][v] += pathFlow; | ||
flow[v][u] -= pathFlow; | ||
} | ||
|
||
maxFlow += pathFlow; | ||
} | ||
|
||
return maxFlow; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import { edmondsKarp } from '../edmondsKarp' | ||
|
||
describe('edmondsKarp', () => { | ||
const init_flow_network = (N: number): number[][] => { | ||
const graph = Array.from({ length: N }, () => Array(N).fill(0)); | ||
return graph; | ||
} | ||
|
||
const add_capacity = ( | ||
graph: number[][], | ||
u: number, | ||
v: number, | ||
capacity: number | ||
) => { | ||
graph[u][v] = capacity; | ||
} | ||
|
||
it('should return the correct maximum flow value for basic graph', () => { | ||
const graph = init_flow_network(6); | ||
add_capacity(graph, 0, 1, 16); | ||
add_capacity(graph, 0, 2, 13); | ||
add_capacity(graph, 1, 2, 10); | ||
add_capacity(graph, 1, 3, 12); | ||
add_capacity(graph, 2, 1, 4); | ||
add_capacity(graph, 2, 4, 14); | ||
add_capacity(graph, 3, 2, 9); | ||
add_capacity(graph, 3, 5, 20); | ||
add_capacity(graph, 4, 3, 7); | ||
add_capacity(graph, 4, 5, 4); | ||
expect(edmondsKarp(graph, 0, 5)).toBe(23); | ||
}); | ||
|
||
it('should return the correct maximum flow value for single element graph', () => { | ||
const graph = init_flow_network(1); | ||
expect(edmondsKarp(graph, 0, 0)).toBe(0); | ||
}); | ||
|
||
const linear_flow_network = init_flow_network(4); | ||
add_capacity(linear_flow_network, 0, 1, 10); | ||
add_capacity(linear_flow_network, 1, 2, 5); | ||
add_capacity(linear_flow_network, 2, 3, 15); | ||
test.each([ | ||
[0, 3, 5], | ||
[0, 2, 5], | ||
[1, 3, 5], | ||
[1, 2, 5], | ||
])( | ||
'correct result for linear flow network with source node %i and sink node %i', | ||
(source, sink, maxFlow) => { | ||
expect(edmondsKarp(linear_flow_network, source, sink)).toBe(maxFlow); | ||
} | ||
); | ||
|
||
const disconnected_flow_network = init_flow_network(4); | ||
add_capacity(disconnected_flow_network, 0, 1, 10); | ||
add_capacity(disconnected_flow_network, 2, 3, 5); | ||
test.each([ | ||
[0, 3, 0], | ||
[1, 2, 0], | ||
[2, 3, 5], | ||
])( | ||
'correct result for disconnected flow network with source node %i and sink node %i', | ||
(source, sink, maxFlow) => { | ||
expect(edmondsKarp(disconnected_flow_network, source, sink)).toBe(maxFlow); | ||
} | ||
); | ||
|
||
const cyclic_flow_network = init_flow_network(5); | ||
add_capacity(cyclic_flow_network, 0, 1, 10); | ||
add_capacity(cyclic_flow_network, 1, 2, 5); | ||
add_capacity(cyclic_flow_network, 2, 0, 7); | ||
add_capacity(cyclic_flow_network, 2, 3, 10); | ||
add_capacity(cyclic_flow_network, 3, 4, 10); | ||
test.each([ | ||
[0, 4, 10], | ||
[1, 4, 10], | ||
[2, 4, 10], | ||
])( | ||
'correct result for cyclic flow network with source node %i and sink node %i', | ||
(source, sink, maxFlow) => { | ||
expect(edmondsKarp(cyclic_flow_network, source, sink)).toBe(maxFlow); | ||
} | ||
); | ||
}); |