diff --git a/DIRECTORY.md b/DIRECTORY.md index ff56d16db947..8fbcbaaec96e 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -353,6 +353,7 @@ - 📄 [PredecessorConstrainedDfs](src/main/java/com/thealgorithms/graph/PredecessorConstrainedDfs.java) - 📄 [StronglyConnectedComponentOptimized](src/main/java/com/thealgorithms/graph/StronglyConnectedComponentOptimized.java) - 📄 [TravelingSalesman](src/main/java/com/thealgorithms/graph/TravelingSalesman.java) + - 📄 [Dinic](src/main/java/com/thealgorithms/graph/Dinic.java) - 📁 **greedyalgorithms** - 📄 [ActivitySelection](src/main/java/com/thealgorithms/greedyalgorithms/ActivitySelection.java) - 📄 [BandwidthAllocation](src/main/java/com/thealgorithms/greedyalgorithms/BandwidthAllocation.java) diff --git a/src/main/java/com/thealgorithms/graph/Dinic.java b/src/main/java/com/thealgorithms/graph/Dinic.java new file mode 100644 index 000000000000..c45bd62ddfbb --- /dev/null +++ b/src/main/java/com/thealgorithms/graph/Dinic.java @@ -0,0 +1,119 @@ +package com.thealgorithms.graph; + +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.Queue; + +/** + * Dinic's algorithm for computing maximum flow in a directed graph. + * + *
Time complexity: O(E * V^2) in the worst case, but typically faster in practice + * and near O(E * sqrt(V)) for unit networks.
+ * + *The graph is represented using a capacity matrix where capacity[u][v] is the + * capacity of the directed edge u -> v. Capacities must be non-negative. + * The algorithm builds level graphs using BFS and finds blocking flows using DFS + * with current-edge optimization.
+ * + *This implementation mirrors the API and validation style of + * {@link EdmondsKarp#maxFlow(int[][], int, int)} for consistency.
+ * + * @see Wikipedia: Dinic's algorithm + */ +public final class Dinic { + private Dinic() { + } + + /** + * Computes the maximum flow from source to sink using Dinic's algorithm. + * + * @param capacity square capacity matrix (n x n); entries must be >= 0 + * @param source source vertex index in [0, n) + * @param sink sink vertex index in [0, n) + * @return the maximum flow value + * @throws IllegalArgumentException if the input matrix is null/non-square/has negatives or + * indices invalid + */ + public static int maxFlow(int[][] capacity, int source, int sink) { + if (capacity == null || capacity.length == 0) { + throw new IllegalArgumentException("Capacity matrix must not be null or empty"); + } + final int n = capacity.length; + for (int i = 0; i < n; i++) { + if (capacity[i] == null || capacity[i].length != n) { + throw new IllegalArgumentException("Capacity matrix must be square"); + } + for (int j = 0; j < n; j++) { + if (capacity[i][j] < 0) { + throw new IllegalArgumentException("Capacities must be non-negative"); + } + } + } + if (source < 0 || sink < 0 || source >= n || sink >= n) { + throw new IllegalArgumentException("Source and sink must be valid vertex indices"); + } + if (source == sink) { + return 0; + } + + // residual capacities + int[][] residual = new int[n][n]; + for (int i = 0; i < n; i++) { + residual[i] = Arrays.copyOf(capacity[i], n); + } + + int[] level = new int[n]; + int flow = 0; + while (bfsBuildLevelGraph(residual, source, sink, level)) { + int[] next = new int[n]; // current-edge optimization + int pushed; + do { + pushed = dfsBlocking(residual, level, next, source, sink, Integer.MAX_VALUE); + flow += pushed; + } while (pushed > 0); + } + return flow; + } + + private static boolean bfsBuildLevelGraph(int[][] residual, int source, int sink, int[] level) { + Arrays.fill(level, -1); + level[source] = 0; + Queue