diff --git a/src/main/java/com/thealgorithms/graph/DisjointSet.java b/src/main/java/com/thealgorithms/graph/DisjointSet.java new file mode 100644 index 000000000000..6eda39d37123 --- /dev/null +++ b/src/main/java/com/thealgorithms/graph/DisjointSet.java @@ -0,0 +1,156 @@ +package com.thealgorithms.graph; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Disjoint Set (Union-Find) Data Structure + * ----------------------------------------- + * This class implements the Disjoint Set Union (DSU) or Union-Find structure, + * which efficiently supports two main operations: + * + * 1. findLeader(x) – Find the representative (leader) of the set that contains element x. + * 2. merge(x, y) – Merge (union) the sets that contain x and y. + * + * Time Complexity (amortized): O(α(N)), where α(N) is the inverse Ackermann function, + * which is very close to constant for all practical input sizes. + */ + +public class DisjointSet { + + int[] parent; // parent[i] stores the parent (or leader) of node i + int[] rank; // rank[i] stores the approximate depth of the tree rooted at i + + /** + * Constructor: Initializes N disjoint sets (each element is its own leader). + * + * @param n the number of elements + */ + public DisjointSet(int n){ + parent = new int[n]; + rank = new int[n]; + for(int i=0; i> accountsMerge(List> accountList) { + int accountListSize = accountList.size(); + DisjointSet dsu = new DisjointSet(accountListSize); + + // Maps email to their component index + Map emailGroup = new HashMap<>(); + + for (int i = 0; i < accountListSize; i++) { + int accountSize = accountList.get(i).size(); + + for (int j = 1; j < accountSize; j++) { + String email = accountList.get(i).get(j); + // String accountName = accountList.get(i).get(0); + + // If this is the first time seeing this email then + // assign component group as the account index + if (!emailGroup.containsKey(email)) { + emailGroup.put(email, i); + } else { + // If we have seen this email before then union this + // group with the previous group of the email + dsu.merge(i, emailGroup.get(email)); + } + } + } + + // Store emails corresponding to the component's representative + Map> components = new HashMap>(); + for (String email : emailGroup.keySet()) { + int group = emailGroup.get(email); + int groupRep = dsu.findLeader(group); + + if (!components.containsKey(groupRep)) { + components.put(groupRep, new ArrayList()); + } + + components.get(groupRep).add(email); + } + + // Sort the components and add the account name + List> mergedAccounts = new ArrayList<>(); + for (int group : components.keySet()) { + List component = components.get(group); + Collections.sort(component); + component.add(0, accountList.get(group).get(0)); + mergedAccounts.add(component); + } + + return mergedAccounts; + } +} \ No newline at end of file diff --git a/src/test/java/com/thealgorithms/graph/DisjointSetTest.java b/src/test/java/com/thealgorithms/graph/DisjointSetTest.java new file mode 100644 index 000000000000..15090fbb11a4 --- /dev/null +++ b/src/test/java/com/thealgorithms/graph/DisjointSetTest.java @@ -0,0 +1,52 @@ +package com.thealgorithms.graph; + + +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.*; + +public class DisjointSetTest { + + @Test + public void testAccountsMerge() { + // Input data setup + List> list = new ArrayList<>(); + list.add(new ArrayList<>(List.of("abc", "abc@mail.com", "abx@mail.com"))); + list.add(new ArrayList<>(List.of("abc", "abc@mail.com", "aby@mail.com"))); + list.add(new ArrayList<>(List.of("Mary", "mary@mail.com"))); + list.add(new ArrayList<>(List.of("John", "johnnybravo@mail.com"))); + list.add(new ArrayList<>(List.of("John", "johnnybravo@mail.com", "john@mail.com"))); + + // Create instance of your Solution/DisjointSet class + DisjointSet disjointSet = new DisjointSet(list.size()); // or DisjointSet if that’s where accountsMerge() lives + + // Execute the method + List> result = disjointSet.accountsMerge(list); + + // Expected output (order of accounts may vary) + List> expected = new ArrayList<>(); + expected.add(Arrays.asList("abc", "abc@mail.com", "abx@mail.com", "aby@mail.com")); + expected.add(Arrays.asList("Mary", "mary@mail.com")); + expected.add(Arrays.asList("John", "john@mail.com", "johnnybravo@mail.com")); + + // Sort both results for deterministic comparison + sortAccounts(result); + sortAccounts(expected); + + // Verify results + assertEquals(expected, result); + } + + // Helper method to sort emails and accounts for deterministic comparison + private static void sortAccounts(List> accounts) { + for (List acc : accounts) { + // Sort all emails (leave name first) + List emails = acc.subList(1, acc.size()); + Collections.sort(emails); + } + // Sort accounts lexicographically by name + accounts.sort(Comparator.comparing(a -> a.get(0))); + } +} + +