diff --git a/DIRECTORY.md b/DIRECTORY.md
index ee09790ed64d..6f63a88b085a 100644
--- a/DIRECTORY.md
+++ b/DIRECTORY.md
@@ -107,6 +107,7 @@
* [ConnectedComponent](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/ConnectedComponent.java)
* [Cycles](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/Cycles.java)
* [DijkstraAlgorithm](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithm.java)
+ * [EdmondsBlossomAlgorithm](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/EdmondsBlossomAlgorithm.java)
* [FloydWarshall](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/FloydWarshall.java)
* [FordFulkerson](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/FordFulkerson.java)
* [Graphs](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/datastructures/graphs/Graphs.java)
@@ -659,6 +660,7 @@
* graphs
* [BoruvkaAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/BoruvkaAlgorithmTest.java)
* [DijkstraAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithmTest.java)
+ * [EdmondsBlossomAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/EdmondsBlossomAlgorithmTest.java)
* [FordFulkersonTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/FordFulkersonTest.java)
* [HamiltonianCycleTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/HamiltonianCycleTest.java)
* [KosarajuTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/KosarajuTest.java)
diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/EdmondsBlossomAlgorithm.java b/src/main/java/com/thealgorithms/datastructures/graphs/EdmondsBlossomAlgorithm.java
new file mode 100644
index 000000000000..27ad96d71876
--- /dev/null
+++ b/src/main/java/com/thealgorithms/datastructures/graphs/EdmondsBlossomAlgorithm.java
@@ -0,0 +1,251 @@
+package com.thealgorithms.datastructures.graphs;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+
+/**
+ * The EdmondsBlossomAlgorithm class implements Edmonds' Blossom Algorithm
+ * to find the maximum matching in a general graph. The algorithm efficiently
+ * handles cases where the graph contains odd-length cycles by contracting
+ * "blossoms" and finding augmenting paths.
+ *
+ * Documentation of Algorithm (Stanford University)
+ *
+ * Wikipedia Documentation
+ */
+public final class EdmondsBlossomAlgorithm {
+
+ private EdmondsBlossomAlgorithm() {
+ }
+
+ private static final int UNMATCHED = -1; // Constant to represent unmatched vertices
+
+ /**
+ * Finds the maximum matching in a general graph (Edmonds Blossom Algorithm).
+ *
+ * @param edges A list of edges in the graph.
+ * @param vertexCount The number of vertices in the graph.
+ * @return A list of matched pairs of vertices.
+ */
+ public static List maximumMatching(List edges, int vertexCount) {
+ List> graph = new ArrayList<>(vertexCount);
+
+ // Initialize each vertex's adjacency list.
+ for (int i = 0; i < vertexCount; i++) {
+ graph.add(new ArrayList<>());
+ }
+
+ // Populate the graph with the edges
+ for (int[] edge : edges) {
+ int u = edge[0];
+ int v = edge[1];
+ graph.get(u).add(v);
+ graph.get(v).add(u);
+ }
+
+ // Initial matching array and auxiliary data structures
+ int[] match = new int[vertexCount];
+ Arrays.fill(match, UNMATCHED); // All vertices are initially unmatched
+ int[] parent = new int[vertexCount];
+ int[] base = new int[vertexCount];
+ boolean[] inBlossom = new boolean[vertexCount]; // Indicates if a vertex is part of a blossom
+ boolean[] inQueue = new boolean[vertexCount]; // Tracks vertices in the BFS queue
+
+ // Main logic for finding maximum matching
+ for (int u = 0; u < vertexCount; u++) {
+ if (match[u] == UNMATCHED) {
+ // BFS initialization
+ Arrays.fill(parent, UNMATCHED);
+ for (int i = 0; i < vertexCount; i++) {
+ base[i] = i; // Each vertex is its own base initially
+ }
+ Arrays.fill(inBlossom, false);
+ Arrays.fill(inQueue, false);
+
+ Queue queue = new LinkedList<>();
+ queue.add(u);
+ inQueue[u] = true;
+
+ boolean augmentingPathFound = false;
+
+ // BFS to find augmenting paths
+ while (!queue.isEmpty() && !augmentingPathFound) {
+ int current = queue.poll(); // Use a different name for clarity
+ for (int y : graph.get(current)) {
+ // Skip if we are looking at the same edge as the current match
+ if (match[current] == y) {
+ continue;
+ }
+
+ if (base[current] == base[y]) {
+ continue; // Avoid self-loops
+ }
+
+ if (parent[y] == UNMATCHED) {
+ // Case 1: y is unmatched, we've found an augmenting path
+ if (match[y] == UNMATCHED) {
+ parent[y] = current;
+ augmentingPathFound = true;
+ updateMatching(match, parent, y); // Augment along this path
+ break;
+ }
+
+ // Case 2: y is matched, add y's match to the queue
+ int z = match[y];
+ parent[y] = current;
+ parent[z] = y;
+ if (!inQueue[z]) {
+ queue.add(z);
+ inQueue[z] = true;
+ }
+ } else {
+ // Case 3: Both x and y have a parent; check for a cycle/blossom
+ int baseU = findBase(base, parent, current, y);
+ if (baseU != UNMATCHED) {
+ contractBlossom(new BlossomData(new BlossomAuxData(queue, parent, base, inBlossom, match, inQueue), current, y, baseU));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Create result list of matched pairs
+ List matchingResult = new ArrayList<>();
+ for (int v = 0; v < vertexCount; v++) {
+ if (match[v] != UNMATCHED && v < match[v]) {
+ matchingResult.add(new int[] {v, match[v]});
+ }
+ }
+
+ return matchingResult;
+ }
+
+ /**
+ * Updates the matching along the augmenting path found.
+ *
+ * @param match The matching array.
+ * @param parent The parent array used during the BFS.
+ * @param u The starting node of the augmenting path.
+ */
+ private static void updateMatching(int[] match, int[] parent, int u) {
+ while (u != UNMATCHED) {
+ int v = parent[u];
+ int next = match[v];
+ match[v] = u;
+ match[u] = v;
+ u = next;
+ }
+ }
+
+ /**
+ * Finds the base of a node in the blossom.
+ *
+ * @param base The base array.
+ * @param parent The parent array.
+ * @param u One end of the edge.
+ * @param v The other end of the edge.
+ * @return The base of the node or UNMATCHED.
+ */
+ private static int findBase(int[] base, int[] parent, int u, int v) {
+ boolean[] visited = new boolean[base.length];
+
+ // Mark ancestors of u
+ int currentU = u;
+ while (true) {
+ currentU = base[currentU]; // Move assignment out of the condition
+ visited[currentU] = true;
+ if (parent[currentU] == UNMATCHED) {
+ break;
+ }
+ currentU = parent[currentU]; // Move assignment out of the condition
+ }
+
+ // Find the common ancestor of v
+ int currentV = v;
+ while (true) {
+ currentV = base[currentV]; // Move assignment out of the condition
+ if (visited[currentV]) {
+ return currentV;
+ }
+ currentV = parent[currentV]; // Move assignment out of the condition
+ }
+ }
+
+ /**
+ * Contracts a blossom and updates the base array.
+ *
+ * @param blossomData The data containing the parameters related to the blossom contraction.
+ */
+ private static void contractBlossom(BlossomData blossomData) {
+ for (int x = blossomData.u; blossomData.auxData.base[x] != blossomData.lca; x = blossomData.auxData.parent[blossomData.auxData.match[x]]) {
+ int baseX = blossomData.auxData.base[x];
+ int matchBaseX = blossomData.auxData.base[blossomData.auxData.match[x]];
+
+ // Split the inner assignment into two separate assignments
+ blossomData.auxData.inBlossom[baseX] = true;
+ blossomData.auxData.inBlossom[matchBaseX] = true;
+ }
+
+ for (int x = blossomData.v; blossomData.auxData.base[x] != blossomData.lca; x = blossomData.auxData.parent[blossomData.auxData.match[x]]) {
+ int baseX = blossomData.auxData.base[x];
+ int matchBaseX = blossomData.auxData.base[blossomData.auxData.match[x]];
+
+ // Split the inner assignment into two separate assignments
+ blossomData.auxData.inBlossom[baseX] = true;
+ blossomData.auxData.inBlossom[matchBaseX] = true;
+ }
+
+ // Update the base for all marked vertices
+ for (int i = 0; i < blossomData.auxData.base.length; i++) {
+ if (blossomData.auxData.inBlossom[blossomData.auxData.base[i]]) {
+ blossomData.auxData.base[i] = blossomData.lca; // Contract to the lowest common ancestor
+ if (!blossomData.auxData.inQueue[i]) {
+ blossomData.auxData.queue.add(i); // Add to queue if not already present
+ blossomData.auxData.inQueue[i] = true;
+ }
+ }
+ }
+ }
+
+ /**
+ * Auxiliary data class to encapsulate common parameters for the blossom operations.
+ */
+ static class BlossomAuxData {
+ Queue queue; // Queue for BFS traversal
+ int[] parent; // Parent array to store the paths
+ int[] base; // Base array to track the base of each vertex
+ boolean[] inBlossom; // Flags to indicate if a vertex is in a blossom
+ int[] match; // Array to store matches for each vertex
+ boolean[] inQueue; // Flags to track vertices in the BFS queue
+
+ BlossomAuxData(Queue queue, int[] parent, int[] base, boolean[] inBlossom, int[] match, boolean[] inQueue) {
+ this.queue = queue;
+ this.parent = parent;
+ this.base = base;
+ this.inBlossom = inBlossom;
+ this.match = match;
+ this.inQueue = inQueue;
+ }
+ }
+
+ /**
+ * BlossomData class with reduced parameters.
+ */
+ static class BlossomData {
+ BlossomAuxData auxData; // Use the auxiliary data class
+ int u; // One vertex in the edge
+ int v; // Another vertex in the edge
+ int lca; // Lowest Common Ancestor
+
+ BlossomData(BlossomAuxData auxData, int u, int v, int lca) {
+ this.auxData = auxData;
+ this.u = u;
+ this.v = v;
+ this.lca = lca;
+ }
+ }
+}
diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/EdmondsBlossomAlgorithmTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/EdmondsBlossomAlgorithmTest.java
new file mode 100644
index 000000000000..4a7232447e50
--- /dev/null
+++ b/src/test/java/com/thealgorithms/datastructures/graphs/EdmondsBlossomAlgorithmTest.java
@@ -0,0 +1,119 @@
+package com.thealgorithms.datastructures.graphs;
+
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Unit tests for the EdmondsBlossomAlgorithm class.
+ *
+ * These tests ensure that the Edmonds' Blossom Algorithm implementation
+ * works as expected for various graph structures, returning the correct
+ * maximum matching.
+ */
+public class EdmondsBlossomAlgorithmTest {
+
+ /**
+ * Helper method to convert a list of matching pairs into a sorted 2D array.
+ * Sorting ensures consistent ordering of pairs and vertices for easier comparison in tests.
+ *
+ * @param matching List of matched pairs returned by the algorithm.
+ * @return A sorted 2D array of matching pairs.
+ */
+ private int[][] convertMatchingToArray(List matching) {
+ // Convert the list of pairs into an array
+ int[][] result = matching.toArray(new int[0][]);
+
+ // Sort each individual pair for consistency
+ for (int[] pair : result) {
+ Arrays.sort(pair);
+ }
+
+ // Sort the array of pairs to ensure consistent order
+ Arrays.sort(result, (a, b) -> Integer.compare(a[0], b[0]));
+ return result;
+ }
+
+ /**
+ * Test Case 1: A triangle graph where vertices 0, 1, and 2 form a cycle.
+ * The expected maximum matching is a single pair (0, 1) or any equivalent pair from the cycle.
+ */
+ @Test
+ public void testCase1() {
+ List edges = Arrays.asList(new int[] {0, 1}, new int[] {1, 2}, new int[] {2, 0});
+ List matching = EdmondsBlossomAlgorithm.maximumMatching(edges, 3);
+
+ int[][] expected = new int[][] {{0, 1}};
+ assertArrayEquals(expected, convertMatchingToArray(matching));
+ }
+
+ /**
+ * Test Case 2: A disconnected graph where vertices 0, 1, 2 form one component,
+ * and vertices 3, 4 form another. The expected maximum matching is two pairs:
+ * (0, 1) and (3, 4).
+ */
+ @Test
+ public void testCase2() {
+ List edges = Arrays.asList(new int[] {0, 1}, new int[] {1, 2}, new int[] {3, 4});
+ List matching = EdmondsBlossomAlgorithm.maximumMatching(edges, 5);
+
+ int[][] expected = new int[][] {{0, 1}, {3, 4}};
+ assertArrayEquals(expected, convertMatchingToArray(matching));
+ }
+
+ /**
+ * Test Case 3: A cycle graph involving vertices 0, 1, 2, 3 forming a cycle,
+ * with an additional edge (4, 5) outside the cycle.
+ * The expected maximum matching is (0, 1) and (4, 5).
+ */
+ @Test
+ public void testCase3() {
+ List edges = Arrays.asList(new int[] {0, 1}, new int[] {1, 2}, new int[] {2, 3}, new int[] {3, 0}, new int[] {4, 5});
+ List matching = EdmondsBlossomAlgorithm.maximumMatching(edges, 6);
+
+ // Updated expected output to include the maximum matching pairs
+ int[][] expected = new int[][] {{0, 1}, {2, 3}, {4, 5}};
+ assertArrayEquals(expected, convertMatchingToArray(matching));
+ }
+
+ /**
+ * Test Case 4: A graph with no edges.
+ * Since there are no edges, the expected matching is an empty set.
+ */
+ @Test
+ public void testCaseNoMatching() {
+ List edges = Collections.emptyList(); // No edges
+ List matching = EdmondsBlossomAlgorithm.maximumMatching(edges, 3);
+
+ int[][] expected = new int[][] {}; // No pairs expected
+ assertArrayEquals(expected, convertMatchingToArray(matching));
+ }
+
+ /**
+ * Test Case 5: A more complex graph with multiple cycles and extra edges.
+ * This tests the algorithm's ability to handle larger, more intricate graphs.
+ * The expected matching is {{0, 1}, {2, 5}, {3, 4}}.
+ */
+ @Test
+ public void testCaseLargeGraph() {
+ List edges = Arrays.asList(new int[] {0, 1}, new int[] {1, 2}, new int[] {2, 3}, new int[] {3, 4}, new int[] {4, 5}, new int[] {5, 0}, new int[] {1, 4}, new int[] {2, 5});
+ List matching = EdmondsBlossomAlgorithm.maximumMatching(edges, 6);
+
+ // Check if the size of the matching is correct (i.e., 3 pairs)
+ assertEquals(3, matching.size());
+
+ // Check that the result contains valid pairs (any order is fine)
+ // Valid maximum matchings could be {{0, 1}, {2, 5}, {3, 4}} or {{0, 1}, {2, 3}, {4, 5}}, etc.
+ int[][] possibleMatching1 = new int[][] {{0, 1}, {2, 5}, {3, 4}};
+ int[][] possibleMatching2 = new int[][] {{0, 1}, {2, 3}, {4, 5}};
+ int[][] result = convertMatchingToArray(matching);
+
+ // Assert that the result is one of the valid maximum matchings
+ assertTrue(Arrays.deepEquals(result, possibleMatching1) || Arrays.deepEquals(result, possibleMatching2));
+ }
+}