-
Notifications
You must be signed in to change notification settings - Fork 20.5k
Implemented Edmonds Blossom Algorithm #5471
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 33 commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
5963b42
Update directory
TarunVishwakarma1 11eb3bc
Implement Edmonds' Blossom Algorithm for maximum matching in general …
TarunVishwakarma1 036983a
Update directory
TarunVishwakarma1 096dee9
Added github path for EdmondsBlossomAlgorith.java in the src/main/jav…
TarunVishwakarma1 164a2ca
Merge remote-tracking branch 'origin/master'
TarunVishwakarma1 7bb9f3a
Merge branch 'TheAlgorithms:master' into master
TarunVishwakarma1 6962cf7
Fixed some build errors
TarunVishwakarma1 c608c8a
Merge remote-tracking branch 'origin/master'
TarunVishwakarma1 324cf61
Update directory
TarunVishwakarma1 00b18dc
Fixed Cland-style
TarunVishwakarma1 9ad53d4
Merge remote-tracking branch 'origin/master'
TarunVishwakarma1 82d45fd
Fixed Cland-style
TarunVishwakarma1 6b6a093
Fixed import statement
TarunVishwakarma1 9eb94bb
Clang Format Fix
TarunVishwakarma1 7bfc151
Fix CheckStyle issues
TarunVishwakarma1 11722ac
Fix CheckStyle issues
TarunVishwakarma1 893aabf
Fixed Clang
TarunVishwakarma1 cb6de45
Fixed Clang
TarunVishwakarma1 b8a9f21
Fixed checkStyle
TarunVishwakarma1 739ae10
Fixed checkStyle
TarunVishwakarma1 ea76d8c
Fixed checkStyle
TarunVishwakarma1 1a0aa7d
Fixed bug
TarunVishwakarma1 02bdf11
Fixed bug
TarunVishwakarma1 63a0b6f
Added Test Class with some tests
TarunVishwakarma1 6aae9b6
Update directory
TarunVishwakarma1 59c3e48
Removed Class instantiation from runTests() method
TarunVishwakarma1 ede9272
Merge remote-tracking branch 'origin/master'
TarunVishwakarma1 8997446
added final to class name and destructured the imports of Test class
TarunVishwakarma1 588d50f
clang format error
TarunVishwakarma1 529c1be
fixed bug in Test class
TarunVishwakarma1 b611727
Please this be last
TarunVishwakarma1 14826e8
Please Please This be last, Added Link to documentation
TarunVishwakarma1 6aeb8e5
Merge branch 'TheAlgorithms:master' into master
TarunVishwakarma1 663bdb5
Merge branch 'master' into master
TarunVishwakarma1 8f5b4b0
Removed System.out and print matching result method
TarunVishwakarma1 5e24873
Merge branch 'master' into master
siriak File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
286 changes: 286 additions & 0 deletions
286
src/main/java/com/thealgorithms/datastructures/graphs/EdmondsBlossomAlgorithm.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,286 @@ | ||
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. | ||
*<p> | ||
* <a href="https://stanford.edu/~rezab/classes/cme323/S16/projects_reports/shoemaker_vare.pdf">Documentation of Algorithm (Stanford University)</a> | ||
* <p></p> | ||
* <a href="https://en.wikipedia.org/wiki/Blossom_algorithm">Wikipedia Documentation</a> | ||
*/ | ||
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<int[]> maximumMatching(List<int[]> edges, int vertexCount) { | ||
List<List<Integer>> 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<Integer> 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<int[]> 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; | ||
} | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Helper method to print the matching results in a readable format. | ||
* | ||
* @param testCase The name of the test case being executed. | ||
* @param matching The list of matched pairs of vertices. | ||
*/ | ||
private static void printMatchingResult(String testCase, List<int[]> matching) { | ||
System.out.print(testCase + " Matching: "); | ||
for (int[] pair : matching) { | ||
System.out.print("(" + pair[0] + ", " + pair[1] + ") "); | ||
} | ||
System.out.println(); | ||
} | ||
|
||
/** | ||
* Auxiliary data class to encapsulate common parameters for the blossom operations. | ||
*/ | ||
static class BlossomAuxData { | ||
Queue<Integer> 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<Integer> 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; | ||
} | ||
} | ||
|
||
/** | ||
* Method to run multiple test cases for the algorithm. | ||
*/ | ||
public static void runTests() { | ||
TarunVishwakarma1 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
// Define test cases | ||
List<int[]> edges1 = Arrays.asList(new int[] {0, 1}, new int[] {1, 2}, new int[] {2, 0}); | ||
List<int[]> edges2 = Arrays.asList(new int[] {0, 1}, new int[] {1, 2}, new int[] {3, 4}); | ||
List<int[]> edges3 = Arrays.asList(new int[] {0, 1}, new int[] {1, 2}, new int[] {2, 3}, new int[] {3, 0}, new int[] {4, 5}); | ||
|
||
// Create an instance of the algorithm | ||
|
||
// Run the test cases | ||
List<List<int[]>> testCases = Arrays.asList(edges1, edges2, edges3); | ||
int vertexCount = 6; // Adjust based on the number of vertices in the test cases | ||
|
||
for (int i = 0; i < testCases.size(); i++) { | ||
List<int[]> matching = maximumMatching(testCases.get(i), vertexCount); | ||
printMatchingResult("Test Case " + (i + 1), matching); | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.