Skip to content

Commit ee4ee22

Browse files
committed
EdmondsKarp algorithm added.
1 parent 914bbee commit ee4ee22

File tree

3 files changed

+175
-1
lines changed

3 files changed

+175
-1
lines changed

src/main/java/com/thealgorithms/graph/BronKerbosch.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* set of vertices adjacent to {@code u}. The algorithm runs in time proportional to the number of
1414
* maximal cliques produced and is widely used for clique enumeration problems.</p>
1515
*
16-
* @author <a href="https://github.com/VictorFrench">Victor French</a>
16+
* @author <a href="https://en.wikipedia.org/wiki/Bron%E2%80%93Kerbosch_algorithm">Wikipedia: Bron–Kerbosch algorithm</a>
1717
*/
1818
public final class BronKerbosch {
1919

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package com.thealgorithms.graph;
2+
3+
import java.util.ArrayDeque;
4+
import java.util.Arrays;
5+
import java.util.Queue;
6+
7+
/**
8+
* Implementation of the Edmonds–Karp algorithm for computing the maximum flow of a directed graph.
9+
* <p>
10+
* The algorithm runs in O(V * E^2) time and is a specific implementation of the Ford–Fulkerson
11+
* method where the augmenting paths are found using breadth-first search (BFS) to ensure the
12+
* shortest augmenting paths (in terms of the number of edges) are used.
13+
* </p>
14+
*
15+
* <p>The graph is represented with a capacity matrix where {@code capacity[u][v]} denotes the
16+
* capacity of the edge from {@code u} to {@code v}. Negative capacities are not allowed.</p>
17+
*
18+
* @author <a href="https://en.wikipedia.org/wiki/Edmonds%E2%80%93Karp_algorithm">Wikipedia: EdmondsKarp algorithm</a>
19+
*/
20+
public final class EdmondsKarp {
21+
22+
private EdmondsKarp() {
23+
}
24+
25+
/**
26+
* Computes the maximum flow from {@code source} to {@code sink} in the provided capacity matrix.
27+
*
28+
* @param capacity the capacity matrix representing the directed graph; must be square and non-null
29+
* @param source the source vertex index
30+
* @param sink the sink vertex index
31+
* @return the value of the maximum flow between {@code source} and {@code sink}
32+
* @throws IllegalArgumentException if the matrix is {@code null}, not square, contains negative
33+
* capacities, or if {@code source} / {@code sink} indices are invalid
34+
*/
35+
public static int maxFlow(int[][] capacity, int source, int sink) {
36+
if (capacity == null || capacity.length == 0) {
37+
throw new IllegalArgumentException("Capacity matrix must not be null or empty");
38+
}
39+
40+
final int n = capacity.length;
41+
for (int row = 0; row < n; row++) {
42+
if (capacity[row] == null || capacity[row].length != n) {
43+
throw new IllegalArgumentException("Capacity matrix must be square");
44+
}
45+
for (int col = 0; col < n; col++) {
46+
if (capacity[row][col] < 0) {
47+
throw new IllegalArgumentException("Capacities must be non-negative");
48+
}
49+
}
50+
}
51+
52+
if (source < 0 || source >= n || sink < 0 || sink >= n) {
53+
throw new IllegalArgumentException("Source and sink must be valid vertex indices");
54+
}
55+
if (source == sink) {
56+
return 0;
57+
}
58+
59+
final int[][] residual = new int[n][n];
60+
for (int i = 0; i < n; i++) {
61+
residual[i] = Arrays.copyOf(capacity[i], n);
62+
}
63+
64+
final int[] parent = new int[n];
65+
int maxFlow = 0;
66+
67+
while (bfs(residual, source, sink, parent)) {
68+
int pathFlow = Integer.MAX_VALUE;
69+
for (int v = sink; v != source; v = parent[v]) {
70+
int u = parent[v];
71+
pathFlow = Math.min(pathFlow, residual[u][v]);
72+
}
73+
74+
for (int v = sink; v != source; v = parent[v]) {
75+
int u = parent[v];
76+
residual[u][v] -= pathFlow;
77+
residual[v][u] += pathFlow;
78+
}
79+
80+
maxFlow += pathFlow;
81+
}
82+
83+
return maxFlow;
84+
}
85+
86+
private static boolean bfs(int[][] residual, int source, int sink, int[] parent) {
87+
Arrays.fill(parent, -1);
88+
parent[source] = source;
89+
90+
Queue<Integer> queue = new ArrayDeque<>();
91+
queue.add(source);
92+
93+
while (!queue.isEmpty()) {
94+
int u = queue.poll();
95+
for (int v = 0; v < residual.length; v++) {
96+
if (residual[u][v] > 0 && parent[v] == -1) {
97+
parent[v] = u;
98+
if (v == sink) {
99+
return true;
100+
}
101+
queue.add(v);
102+
}
103+
}
104+
}
105+
return false;
106+
}
107+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.thealgorithms.graph;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertThrows;
5+
import org.junit.jupiter.api.DisplayName;
6+
import org.junit.jupiter.api.Test;
7+
8+
class EdmondsKarpTest {
9+
10+
@Test
11+
@DisplayName("Classic CLRS network yields max flow 23")
12+
void clrsExample() {
13+
int[][] capacity = {
14+
{0, 16, 13, 0, 0, 0},
15+
{0, 0, 10, 12, 0, 0},
16+
{0, 4, 0, 0, 14, 0},
17+
{0, 0, 9, 0, 0, 20},
18+
{0, 0, 0, 7, 0, 4},
19+
{0, 0, 0, 0, 0, 0}
20+
};
21+
int maxFlow = EdmondsKarp.maxFlow(capacity, 0, 5);
22+
assertEquals(23, maxFlow);
23+
}
24+
25+
@Test
26+
@DisplayName("Disconnected network has zero flow")
27+
void disconnectedGraph() {
28+
int[][] capacity = {
29+
{0, 0, 0},
30+
{0, 0, 0},
31+
{0, 0, 0}
32+
};
33+
int maxFlow = EdmondsKarp.maxFlow(capacity, 0, 2);
34+
assertEquals(0, maxFlow);
35+
}
36+
37+
@Test
38+
@DisplayName("Source equals sink returns zero")
39+
void sourceEqualsSink() {
40+
int[][] capacity = {
41+
{0, 5},
42+
{0, 0}
43+
};
44+
int maxFlow = EdmondsKarp.maxFlow(capacity, 0, 0);
45+
assertEquals(0, maxFlow);
46+
}
47+
48+
@Test
49+
@DisplayName("Invalid matrix throws exception")
50+
void invalidMatrix() {
51+
int[][] capacity = {
52+
{0, 1},
53+
{1}
54+
};
55+
assertThrows(IllegalArgumentException.class, () -> EdmondsKarp.maxFlow(capacity, 0, 1));
56+
}
57+
58+
@Test
59+
@DisplayName("Negative capacity is rejected")
60+
void negativeCapacity() {
61+
int[][] capacity = {
62+
{0, -1},
63+
{0, 0}
64+
};
65+
assertThrows(IllegalArgumentException.class, () -> EdmondsKarp.maxFlow(capacity, 0, 1));
66+
}
67+
}

0 commit comments

Comments
 (0)