Skip to content

Commit ef75cc4

Browse files
committed
Docs: add Dinic reference link and apply clang-format
1 parent b1074ba commit ef75cc4

File tree

2 files changed

+137
-149
lines changed

2 files changed

+137
-149
lines changed

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

Lines changed: 85 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -17,96 +17,101 @@
1717
*
1818
* <p>This implementation mirrors the API and validation style of
1919
* {@link EdmondsKarp#maxFlow(int[][], int, int)} for consistency.</p>
20+
*
21+
* @see <a href="https://en.wikipedia.org/wiki/Dinic%27s_algorithm">Wikipedia: Dinic's algorithm</a>
2022
*/
2123
public final class Dinic {
24+
private Dinic() {}
2225

23-
private Dinic() {}
24-
25-
/**
26-
* Computes the maximum flow from source to sink using Dinic's algorithm.
27-
*
28-
* @param capacity square capacity matrix (n x n); entries must be >= 0
29-
* @param source source vertex index in [0, n)
30-
* @param sink sink vertex index in [0, n)
31-
* @return the maximum flow value
32-
* @throws IllegalArgumentException if the input matrix is null/non-square/has negatives or indices invalid
33-
*/
34-
public static int maxFlow(int[][] capacity, int source, int sink) {
35-
if (capacity == null || capacity.length == 0) {
36-
throw new IllegalArgumentException("Capacity matrix must not be null or empty");
37-
}
38-
final int n = capacity.length;
39-
for (int i = 0; i < n; i++) {
40-
if (capacity[i] == null || capacity[i].length != n) {
41-
throw new IllegalArgumentException("Capacity matrix must be square");
42-
}
43-
for (int j = 0; j < n; j++) {
44-
if (capacity[i][j] < 0) {
45-
throw new IllegalArgumentException("Capacities must be non-negative");
46-
}
47-
}
48-
}
49-
if (source < 0 || sink < 0 || source >= n || sink >= n) {
50-
throw new IllegalArgumentException("Source and sink must be valid vertex indices");
51-
}
52-
if (source == sink) {
53-
return 0;
26+
/**
27+
* Computes the maximum flow from source to sink using Dinic's algorithm.
28+
*
29+
* @param capacity square capacity matrix (n x n); entries must be >= 0
30+
* @param source source vertex index in [0, n)
31+
* @param sink sink vertex index in [0, n)
32+
* @return the maximum flow value
33+
* @throws IllegalArgumentException if the input matrix is null/non-square/has negatives or
34+
* indices invalid
35+
*/
36+
public static int maxFlow(int[][] capacity, int source, int sink) {
37+
if (capacity == null || capacity.length == 0) {
38+
throw new IllegalArgumentException("Capacity matrix must not be null or empty");
39+
}
40+
final int n = capacity.length;
41+
for (int i = 0; i < n; i++) {
42+
if (capacity[i] == null || capacity[i].length != n) {
43+
throw new IllegalArgumentException("Capacity matrix must be square");
44+
}
45+
for (int j = 0; j < n; j++) {
46+
if (capacity[i][j] < 0) {
47+
throw new IllegalArgumentException("Capacities must be non-negative");
5448
}
49+
}
50+
}
51+
if (source < 0 || sink < 0 || source >= n || sink >= n) {
52+
throw new IllegalArgumentException("Source and sink must be valid vertex indices");
53+
}
54+
if (source == sink) {
55+
return 0;
56+
}
5557

56-
// residual capacities
57-
int[][] residual = new int[n][n];
58-
for (int i = 0; i < n; i++) {
59-
residual[i] = Arrays.copyOf(capacity[i], n);
60-
}
58+
// residual capacities
59+
int[][] residual = new int[n][n];
60+
for (int i = 0; i < n; i++) {
61+
residual[i] = Arrays.copyOf(capacity[i], n);
62+
}
6163

62-
int[] level = new int[n];
63-
int flow = 0;
64-
while (bfsBuildLevelGraph(residual, source, sink, level)) {
65-
int[] next = new int[n]; // current-edge optimization
66-
int pushed;
67-
do {
68-
pushed = dfsBlocking(residual, level, next, source, sink, Integer.MAX_VALUE);
69-
flow += pushed;
70-
} while (pushed > 0);
71-
}
72-
return flow;
64+
int[] level = new int[n];
65+
int flow = 0;
66+
while (bfsBuildLevelGraph(residual, source, sink, level)) {
67+
int[] next = new int[n]; // current-edge optimization
68+
int pushed;
69+
do {
70+
pushed = dfsBlocking(residual, level, next, source, sink, Integer.MAX_VALUE);
71+
flow += pushed;
72+
} while (pushed > 0);
7373
}
74+
return flow;
75+
}
7476

75-
private static boolean bfsBuildLevelGraph(int[][] residual, int source, int sink, int[] level) {
76-
Arrays.fill(level, -1);
77-
level[source] = 0;
78-
Queue<Integer> q = new ArrayDeque<>();
79-
q.add(source);
80-
while (!q.isEmpty()) {
81-
int u = q.poll();
82-
for (int v = 0; v < residual.length; v++) {
83-
if (residual[u][v] > 0 && level[v] == -1) {
84-
level[v] = level[u] + 1;
85-
if (v == sink) {
86-
return true;
87-
}
88-
q.add(v);
89-
}
90-
}
77+
private static boolean bfsBuildLevelGraph(int[][] residual, int source, int sink, int[] level) {
78+
Arrays.fill(level, -1);
79+
level[source] = 0;
80+
Queue<Integer> q = new ArrayDeque<>();
81+
q.add(source);
82+
while (!q.isEmpty()) {
83+
int u = q.poll();
84+
for (int v = 0; v < residual.length; v++) {
85+
if (residual[u][v] > 0 && level[v] == -1) {
86+
level[v] = level[u] + 1;
87+
if (v == sink) {
88+
return true;
89+
}
90+
q.add(v);
9191
}
92-
return level[sink] != -1;
92+
}
9393
}
94+
return level[sink] != -1;
95+
}
9496

95-
private static int dfsBlocking(int[][] residual, int[] level, int[] next, int u, int sink, int f) {
96-
if (u == sink) {
97-
return f;
98-
}
99-
final int n = residual.length;
100-
for (int v = next[u]; v < n; v++, next[u] = v) {
101-
if (residual[u][v] <= 0) continue;
102-
if (level[v] != level[u] + 1) continue;
103-
int pushed = dfsBlocking(residual, level, next, v, sink, Math.min(f, residual[u][v]));
104-
if (pushed > 0) {
105-
residual[u][v] -= pushed;
106-
residual[v][u] += pushed;
107-
return pushed;
108-
}
109-
}
110-
return 0;
97+
private static int dfsBlocking(
98+
int[][] residual, int[] level, int[] next, int u, int sink, int f) {
99+
if (u == sink) {
100+
return f;
101+
}
102+
final int n = residual.length;
103+
for (int v = next[u]; v < n; v++, next[u] = v) {
104+
if (residual[u][v] <= 0)
105+
continue;
106+
if (level[v] != level[u] + 1)
107+
continue;
108+
int pushed = dfsBlocking(residual, level, next, v, sink, Math.min(f, residual[u][v]));
109+
if (pushed > 0) {
110+
residual[u][v] -= pushed;
111+
residual[v][u] += pushed;
112+
return pushed;
113+
}
111114
}
115+
return 0;
116+
}
112117
}

src/test/java/com/thealgorithms/graph/DinicTest.java

Lines changed: 52 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -7,82 +7,65 @@
77
import org.junit.jupiter.api.Test;
88

99
class DinicTest {
10+
@Test
11+
@DisplayName("Classic CLRS network yields max flow 23 (Dinic)")
12+
void clrsExample() {
13+
int[][] capacity = {{0, 16, 13, 0, 0, 0}, {0, 0, 10, 12, 0, 0}, {0, 4, 0, 0, 14, 0},
14+
{0, 0, 9, 0, 0, 20}, {0, 0, 0, 7, 0, 4}, {0, 0, 0, 0, 0, 0}};
15+
int maxFlow = Dinic.maxFlow(capacity, 0, 5);
16+
assertEquals(23, maxFlow);
17+
}
1018

11-
@Test
12-
@DisplayName("Classic CLRS network yields max flow 23 (Dinic)")
13-
void clrsExample() {
14-
int[][] capacity = {
15-
{0, 16, 13, 0, 0, 0},
16-
{0, 0, 10, 12, 0, 0},
17-
{0, 4, 0, 0, 14, 0},
18-
{0, 0, 9, 0, 0, 20},
19-
{0, 0, 0, 7, 0, 4},
20-
{0, 0, 0, 0, 0, 0}
21-
};
22-
int maxFlow = Dinic.maxFlow(capacity, 0, 5);
23-
assertEquals(23, maxFlow);
24-
}
25-
26-
@Test
27-
@DisplayName("Disconnected network has zero flow (Dinic)")
28-
void disconnectedGraph() {
29-
int[][] capacity = {
30-
{0, 0, 0},
31-
{0, 0, 0},
32-
{0, 0, 0}
33-
};
34-
int maxFlow = Dinic.maxFlow(capacity, 0, 2);
35-
assertEquals(0, maxFlow);
36-
}
19+
@Test
20+
@DisplayName("Disconnected network has zero flow (Dinic)")
21+
void disconnectedGraph() {
22+
int[][] capacity = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}};
23+
int maxFlow = Dinic.maxFlow(capacity, 0, 2);
24+
assertEquals(0, maxFlow);
25+
}
3726

38-
@Test
39-
@DisplayName("Source equals sink returns zero (Dinic)")
40-
void sourceEqualsSink() {
41-
int[][] capacity = {
42-
{0, 5},
43-
{0, 0}
44-
};
45-
int maxFlow = Dinic.maxFlow(capacity, 0, 0);
46-
assertEquals(0, maxFlow);
47-
}
27+
@Test
28+
@DisplayName("Source equals sink returns zero (Dinic)")
29+
void sourceEqualsSink() {
30+
int[][] capacity = {{0, 5}, {0, 0}};
31+
int maxFlow = Dinic.maxFlow(capacity, 0, 0);
32+
assertEquals(0, maxFlow);
33+
}
4834

49-
@Test
50-
@DisplayName("Invalid matrix throws exception (Dinic)")
51-
void invalidMatrix() {
52-
int[][] capacity = {
53-
{0, 1},
54-
{1}
55-
};
56-
assertThrows(IllegalArgumentException.class, () -> Dinic.maxFlow(capacity, 0, 1));
57-
}
35+
@Test
36+
@DisplayName("Invalid matrix throws exception (Dinic)")
37+
void invalidMatrix() {
38+
int[][] capacity = {{0, 1}, {1}};
39+
assertThrows(IllegalArgumentException.class, () -> Dinic.maxFlow(capacity, 0, 1));
40+
}
5841

59-
@Test
60-
@DisplayName("Dinic matches Edmonds-Karp on random small graphs")
61-
void parityWithEdmondsKarp() {
62-
java.util.Random rnd = new java.util.Random(42);
63-
for (int n = 3; n <= 7; n++) {
64-
for (int it = 0; it < 25; it++) {
65-
int[][] cap = new int[n][n];
66-
for (int i = 0; i < n; i++) {
67-
for (int j = 0; j < n; j++) {
68-
if (i != j && rnd.nextDouble() < 0.35) {
69-
cap[i][j] = rnd.nextInt(10); // capacities 0..9
70-
}
71-
}
72-
}
73-
int s = 0, t = n - 1;
74-
int f1 = Dinic.maxFlow(copyMatrix(cap), s, t);
75-
int f2 = EdmondsKarp.maxFlow(cap, s, t);
76-
assertEquals(f2, f1);
42+
@Test
43+
@DisplayName("Dinic matches Edmonds-Karp on random small graphs")
44+
void parityWithEdmondsKarp() {
45+
java.util.Random rnd = new java.util.Random(42);
46+
for (int n = 3; n <= 7; n++) {
47+
for (int it = 0; it < 25; it++) {
48+
int[][] cap = new int[n][n];
49+
for (int i = 0; i < n; i++) {
50+
for (int j = 0; j < n; j++) {
51+
if (i != j && rnd.nextDouble() < 0.35) {
52+
cap[i][j] = rnd.nextInt(10); // capacities 0..9
7753
}
54+
}
7855
}
56+
int s = 0, t = n - 1;
57+
int f1 = Dinic.maxFlow(copyMatrix(cap), s, t);
58+
int f2 = EdmondsKarp.maxFlow(cap, s, t);
59+
assertEquals(f2, f1);
60+
}
7961
}
62+
}
8063

81-
private static int[][] copyMatrix(int[][] a) {
82-
int[][] b = new int[a.length][a.length];
83-
for (int i = 0; i < a.length; i++) {
84-
b[i] = java.util.Arrays.copyOf(a[i], a[i].length);
85-
}
86-
return b;
64+
private static int[][] copyMatrix(int[][] a) {
65+
int[][] b = new int[a.length][a.length];
66+
for (int i = 0; i < a.length; i++) {
67+
b[i] = java.util.Arrays.copyOf(a[i], a[i].length);
8768
}
69+
return b;
70+
}
8871
}

0 commit comments

Comments
 (0)