diff --git a/DIRECTORY.md b/DIRECTORY.md index 0e96e8fb22bf..ee09790ed64d 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -265,6 +265,7 @@ * [ActivitySelection](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/greedyalgorithms/ActivitySelection.java) * [CoinChange](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/greedyalgorithms/CoinChange.java) * [FractionalKnapsack](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/greedyalgorithms/FractionalKnapsack.java) + * [GaleShapley](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/greedyalgorithms/GaleShapley.java) * [JobSequencing](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/greedyalgorithms/JobSequencing.java) * [MinimizingLateness](https://github.com/TheAlgorithms/Java/blob/master/src/main/java/com/thealgorithms/greedyalgorithms/MinimizingLateness.java) * io @@ -749,6 +750,7 @@ * [ActivitySelectionTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/greedyalgorithms/ActivitySelectionTest.java) * [CoinChangeTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/greedyalgorithms/CoinChangeTest.java) * [FractionalKnapsackTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/greedyalgorithms/FractionalKnapsackTest.java) + * [GaleShapleyTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/greedyalgorithms/GaleShapleyTest.java) * [JobSequencingTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/greedyalgorithms/JobSequencingTest.java) * [MinimizingLatenessTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/greedyalgorithms/MinimizingLatenessTest.java) * io diff --git a/src/main/java/com/thealgorithms/greedyalgorithms/GaleShapley.java b/src/main/java/com/thealgorithms/greedyalgorithms/GaleShapley.java new file mode 100644 index 000000000000..a4a0366375eb --- /dev/null +++ b/src/main/java/com/thealgorithms/greedyalgorithms/GaleShapley.java @@ -0,0 +1,65 @@ +package com.thealgorithms.greedyalgorithms; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; + +/** + * Implementation of the Gale-Shapley Algorithm for Stable Matching. + * Problem link: https://en.wikipedia.org/wiki/Stable_marriage_problem + */ +public final class GaleShapley { + + private GaleShapley() { + } + + /** + * Function to find stable matches between men and women. + * + * @param womenPrefs A map containing women's preferences where each key is a woman and the value is an array of men in order of preference. + * @param menPrefs A map containing men's preferences where each key is a man and the value is an array of women in order of preference. + * @return A map containing stable matches where the key is a woman and the value is her matched man. + */ + public static Map stableMatch(Map> womenPrefs, Map> menPrefs) { + // Initialize all men as free + Map engagements = new HashMap<>(); + LinkedList freeMen = new LinkedList<>(menPrefs.keySet()); + + // While there are free men + while (!freeMen.isEmpty()) { + String man = freeMen.poll(); // Get the first free man + LinkedList manPref = menPrefs.get(man); // Get the preferences of the man + + // Check if manPref is null or empty + if (manPref == null || manPref.isEmpty()) { + continue; // Skip if no preferences + } + + // Propose to the first woman in the man's preference list + String woman = manPref.poll(); + String fiance = engagements.get(woman); + + // If the woman is not engaged, engage her with the current man + if (fiance == null) { + engagements.put(woman, man); + } else { + // If the woman prefers the current man over her current fiance + LinkedList womanPrefList = womenPrefs.get(woman); + + // Check if womanPrefList is null + if (womanPrefList == null) { + continue; // Skip if no preferences for the woman + } + + if (womanPrefList.indexOf(man) < womanPrefList.indexOf(fiance)) { + engagements.put(woman, man); + freeMen.add(fiance); // Previous fiance becomes free + } else { + // Woman rejects the new proposal, the man remains free + freeMen.add(man); + } + } + } + return engagements; // Return the stable matches + } +} diff --git a/src/test/java/com/thealgorithms/greedyalgorithms/GaleShapleyTest.java b/src/test/java/com/thealgorithms/greedyalgorithms/GaleShapleyTest.java new file mode 100644 index 000000000000..fcd173202469 --- /dev/null +++ b/src/test/java/com/thealgorithms/greedyalgorithms/GaleShapleyTest.java @@ -0,0 +1,72 @@ +package com.thealgorithms.greedyalgorithms; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.Test; + +public class GaleShapleyTest { + + @Test + public void testStableMatch() { + Map> womenPrefs = new HashMap<>(); + womenPrefs.put("A", new LinkedList<>(List.of("X", "Y", "Z"))); + womenPrefs.put("B", new LinkedList<>(List.of("Y", "X", "Z"))); + womenPrefs.put("C", new LinkedList<>(List.of("X", "Y", "Z"))); + + Map> menPrefs = new HashMap<>(); + menPrefs.put("X", new LinkedList<>(List.of("A", "B", "C"))); + menPrefs.put("Y", new LinkedList<>(List.of("B", "A", "C"))); + menPrefs.put("Z", new LinkedList<>(List.of("A", "B", "C"))); + + Map result = GaleShapley.stableMatch(womenPrefs, menPrefs); + + Map expected = new HashMap<>(); + expected.put("A", "X"); + expected.put("B", "Y"); + expected.put("C", "Z"); + + assertEquals(expected, result); + } + + @Test + public void testSinglePair() { + Map> womenPrefs = new HashMap<>(); + womenPrefs.put("A", new LinkedList<>(List.of("X"))); + + Map> menPrefs = new HashMap<>(); + menPrefs.put("X", new LinkedList<>(List.of("A"))); + + Map result = GaleShapley.stableMatch(womenPrefs, menPrefs); + + Map expected = new HashMap<>(); + expected.put("A", "X"); + + assertEquals(expected, result); + } + + @Test + public void testEqualPreferences() { + Map> womenPrefs = new HashMap<>(); + womenPrefs.put("A", new LinkedList<>(List.of("X", "Y", "Z"))); + womenPrefs.put("B", new LinkedList<>(List.of("X", "Y", "Z"))); + womenPrefs.put("C", new LinkedList<>(List.of("X", "Y", "Z"))); + + Map> menPrefs = new HashMap<>(); + menPrefs.put("X", new LinkedList<>(List.of("A", "B", "C"))); + menPrefs.put("Y", new LinkedList<>(List.of("A", "B", "C"))); + menPrefs.put("Z", new LinkedList<>(List.of("A", "B", "C"))); + + Map result = GaleShapley.stableMatch(womenPrefs, menPrefs); + + Map expected = new HashMap<>(); + expected.put("A", "X"); + expected.put("B", "Y"); + expected.put("C", "Z"); + + assertEquals(expected, result); + } +}