diff --git a/src/main/java/com/thealgorithms/graph/StoerWagner.java b/src/main/java/com/thealgorithms/graph/StoerWagner.java new file mode 100644 index 000000000000..b204834c431a --- /dev/null +++ b/src/main/java/com/thealgorithms/graph/StoerWagner.java @@ -0,0 +1,78 @@ +package com.thealgorithms.graph; + +/** + * An implementation of the Stoer-Wagner algorithm to find the global minimum cut of an undirected, weighted graph. + * A minimum cut is a partition of the graph's vertices into two disjoint sets with the minimum possible edge weight + * sum connecting the two sets. + * + * Wikipedia: https://en.wikipedia.org/wiki/Stoer%E2%80%93Wagner_algorithm + * Time Complexity: O(V^3) where V is the number of vertices. + */ +public class StoerWagner { + + /** + * Finds the minimum cut in the given undirected, weighted graph. + * + * @param graph An adjacency matrix representing the graph. graph[i][j] is the weight of the edge between i and j. + * @return The weight of the minimum cut. + */ + public int findMinCut(int[][] graph) { + int n = graph.length; + if (n < 2) { + return 0; + } + + int[][] currentGraph = new int[n][n]; + for (int i = 0; i < n; i++) { + System.arraycopy(graph[i], 0, currentGraph[i], 0, n); + } + + int minCut = Integer.MAX_VALUE; + boolean[] merged = new boolean[n]; + + for (int phase = 0; phase < n - 1; phase++) { + boolean[] inSetA = new boolean[n]; + int[] weights = new int[n]; + int prev = -1; + int last = -1; + + for (int i = 0; i < n - phase; i++) { + int maxWeight = -1; + int currentVertex = -1; + + for (int j = 0; j < n; j++) { + if (!merged[j] && !inSetA[j] && weights[j] > maxWeight) { + maxWeight = weights[j]; + currentVertex = j; + } + } + + if (currentVertex == -1) { + // This can happen if the graph is disconnected. + return 0; + } + + prev = last; + last = currentVertex; + inSetA[last] = true; + + for (int j = 0; j < n; j++) { + if (!merged[j] && !inSetA[j]) { + weights[j] += currentGraph[last][j]; + } + } + } + + minCut = Math.min(minCut, weights[last]); + + // Merge 'last' vertex into 'prev' vertex + for (int i = 0; i < n; i++) { + currentGraph[prev][i] += currentGraph[last][i]; + currentGraph[i][prev] = currentGraph[prev][i]; + } + merged[last] = true; + } + + return minCut; + } +} diff --git a/src/test/java/com/thealgorithms/graph/StoerWagnerTest.java b/src/test/java/com/thealgorithms/graph/StoerWagnerTest.java new file mode 100644 index 000000000000..894d99687d1d --- /dev/null +++ b/src/test/java/com/thealgorithms/graph/StoerWagnerTest.java @@ -0,0 +1,64 @@ +package com.thealgorithms.graph; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; + +/** + * Unit tests for the StoerWagner global minimum cut algorithm. + * + * These tests verify correctness of the implementation across + * several graph configurations: simple, complete, disconnected, + * and small edge cases. + */ +public class StoerWagnerTest { + + @Test + public void testSimpleGraph() { + int[][] graph = {{0, 3, 2, 0}, {3, 0, 1, 4}, {2, 1, 0, 5}, {0, 4, 5, 0}}; + StoerWagner algo = new StoerWagner(); + assertEquals(5, algo.findMinCut(graph)); // Correct minimum cut = 5 + } + + @Test + public void testTriangleGraph() { + int[][] graph = {{0, 2, 3}, {2, 0, 4}, {3, 4, 0}}; + StoerWagner algo = new StoerWagner(); + assertEquals(5, algo.findMinCut(graph)); // min cut = 5 + } + + @Test + public void testDisconnectedGraph() { + int[][] graph = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; + StoerWagner algo = new StoerWagner(); + assertEquals(0, algo.findMinCut(graph)); // Disconnected graph => cut = 0 + } + + @Test + public void testCompleteGraph() { + int[][] graph = {{0, 1, 1, 1}, {1, 0, 1, 1}, {1, 1, 0, 1}, {1, 1, 1, 0}}; + StoerWagner algo = new StoerWagner(); + assertEquals(3, algo.findMinCut(graph)); // Each vertex connected to all others + } + + @Test + public void testSingleVertex() { + int[][] graph = {{0}}; + StoerWagner algo = new StoerWagner(); + assertEquals(0, algo.findMinCut(graph)); // Only one vertex + } + + @Test + public void testTwoVertices() { + int[][] graph = {{0, 7}, {7, 0}}; + StoerWagner algo = new StoerWagner(); + assertEquals(7, algo.findMinCut(graph)); // Only one edge, cut weight = 7 + } + + @Test + public void testSquareGraphWithDiagonal() { + int[][] graph = {{0, 2, 0, 2}, {2, 0, 3, 0}, {0, 3, 0, 4}, {2, 0, 4, 0}}; + StoerWagner algo = new StoerWagner(); + assertEquals(4, algo.findMinCut(graph)); // verified manually + } +}