199. Accounts Merge
🔗 LeetCode Problem: 721. Accounts Merge
📊 Difficulty: Medium
🏷️ Topics: Graph, Union-Find, DFS, String, Hash Table
Problem Statement
Given a list of accounts where each element accounts[i] is a list of strings, where the first element accounts[i][0] is a name, and the rest of the elements are emails representing emails of the account.
Now, we would like to merge these accounts. Two accounts definitely belong to the same person if there is some common email to both accounts. Note that even if two accounts have the same name, they may belong to different people as people could have the same name. A person can have any number of accounts initially, but all of their accounts definitely have the same name.
After merging the accounts, return the accounts in the following format: the first element of each account is the name, and the rest of the elements are emails in sorted order. The accounts themselves can be returned in any order.
Example 1:
Input: accounts = [
["John","johnsmith@mail.com","john_newyork@mail.com"],
["John","johnsmith@mail.com","john00@mail.com"],
["Mary","mary@mail.com"],
["John","johnnybravo@mail.com"]
]
Output: [
["John","john00@mail.com","john_newyork@mail.com","johnsmith@mail.com"],
["Mary","mary@mail.com"],
["John","johnnybravo@mail.com"]
]
Explanation:
First two "John" accounts share johnsmith@mail.com → merge!
Third "Mary" is separate
Fourth "John" has no common emails with first two → separate
Result: 3 accounts after merging
Example 2:
Input: accounts = [
["Gabe","Gabe0@m.co","Gabe3@m.co","Gabe1@m.co"],
["Kevin","Kevin3@m.co","Kevin5@m.co","Kevin0@m.co"],
["Ethan","Ethan5@m.co","Ethan4@m.co","Ethan0@m.co"],
["Hanah","Hanah3@m.co","Hanah1@m.co","Hanah0@m.co"],
["Gabe","Gabe0@m.co","Gabe4@m.co","Gabe2@m.co"]
]
Output: [
["Ethan","Ethan0@m.co","Ethan4@m.co","Ethan5@m.co"],
["Gabe","Gabe0@m.co","Gabe1@m.co","Gabe2@m.co","Gabe3@m.co","Gabe4@m.co"],
["Hanah","Hanah0@m.co","Hanah1@m.co","Hanah3@m.co"],
["Kevin","Kevin0@m.co","Kevin3@m.co","Kevin5@m.co"]
]
Explanation:
First and last "Gabe" accounts share Gabe0@m.co → merge!
All unique emails from both accounts combined
Constraints:
- 1 <= accounts.length <= 1000
- 2 <= accounts[i].length <= 10
- 1 <= accounts[i][j].length <= 30
- accounts[i][0] consists of English letters.
- accounts[i][j] (for j > 0) is a valid email.
🌳 Visual Understanding - The Complete Picture
The Problem We're Solving 🤔
Given accounts with emails, merge accounts by common emails: 📧
Key insight:
If two accounts share ANY email → same person! 🎯
Even if names are same, no shared email → different people!
Example:
Account 1: John [a@mail.com, b@mail.com]
Account 2: John [b@mail.com, c@mail.com]
Account 3: John [d@mail.com]
Account 1 & 2: Share b@mail.com → MERGE! ✅
Account 3: No shared emails → SEPARATE! ❌
Result:
John [a@mail.com, b@mail.com, c@mail.com] (merged)
John [d@mail.com] (separate)
Common email = connection! 🔗
Understanding the Merging Process 🔄
Think of it like social networks: 👥
If Alice has emails [a1, a2] and Bob has [a2, a3]:
They share a2 → must be same person!
(Bob might be Alice's nickname or maiden name)
Visual:
Account A: [John, a@mail.com, b@mail.com]
↓
shares b@mail.com
↓
Account B: [John, b@mail.com, c@mail.com]
Merged: [John, a@mail.com, b@mail.com, c@mail.com]
Key point: Transitive merging! 🌊
If A shares with B, and B shares with C
Then A, B, C all belong to same person!
A ↔ B ↔ C → {A, B, C}
Why Union-Find is Perfect! 💡
This is GROUPING problem! 🎯
Accounts are like nodes
Common emails are like edges
Example:
Account 0: [a@, b@]
Account 1: [b@, c@]
Account 2: [d@]
Email b@ connects accounts 0 and 1!
Account 2 has no connections
Groups: {0, 1} and {2}
Union-Find perfect for grouping! 🔗
Process:
1. For each email, track which accounts have it
2. If email appears in multiple accounts → union them!
3. Group accounts by their leader
4. Collect all emails for each group
Elegant solution! ✨
🧠 The Journey - Building Intuition
Part 1: Real World Problem - "Email Accounts" 📧
💭 Imagine your email inbox situation:
You have multiple email accounts:
- Personal: alice@gmail.com, alice.smith@gmail.com
- Work: alice.smith@company.com
- Old: alice123@yahoo.com
You used different combinations:
Account 1: [alice@gmail.com, alice.smith@gmail.com]
Account 2: [alice.smith@gmail.com, alice.smith@company.com]
Account 3: [alice123@yahoo.com]
Question: Which accounts belong to same person?
Reasoning:
Account 1 & 2 share alice.smith@gmail.com → SAME PERSON! ✅
Account 3 has no overlap → DIFFERENT? OR SAME?
Only by shared emails can we tell!
Result:
Person 1: All emails from accounts 1 & 2 merged
Person 2: Emails from account 3
This is EXACTLY our problem! 🎯
Part 2: The Naive Way - "Check All Pairs" 🤔
💭 Brute force: Check every pair of accounts!
For each pair of accounts:
Compare their emails
If any email matches → merge them
Example:
A1: [a@, b@]
A2: [b@, c@]
A3: [d@]
Compare A1 vs A2: b@ matches! Merge ✅
Compare A1 vs A3: No match ❌
Compare A2 vs A3: No match ❌
But what if chain merging?
A1 shares with A2
A2 shares with A3
A3 shares with A4
Need to keep merging until no more merges!
Complicated and slow! ❌
There must be better way... 💡
Part 3: The Graph View - "Emails as Connections" 🔗
💭 Key insight: Think of emails as BRIDGES!
Each account is a node
Shared email creates edge between accounts
Example:
Account 0: [a@, b@]
Account 1: [b@, c@]
Account 2: [c@, d@]
Graph:
0 --- 1 --- 2 (connected by shared emails)
b@ connects 0 and 1
c@ connects 1 and 2
All three are connected (same person)!
This is connected components problem! 🌊
Could use DFS to find components...
But Union-Find is more elegant! 🌟
Part 4: Union-Find Perspective - "Group by Email" 💡
💭 Beautiful Union-Find approach!
Strategy:
1. For each unique email, track which accounts have it
2. For accounts sharing an email, union them!
3. Accounts with same leader = same person
Example:
Account 0: [a@, b@]
Account 1: [b@, c@]
Account 2: [d@]
Email tracking:
a@: appears in account 0
b@: appears in accounts 0, 1 ← UNION 0 and 1!
c@: appears in account 1
d@: appears in account 2
After unions:
Accounts 0 and 1 have same leader (merged!)
Account 2 separate
Groups by leader:
Leader 0: accounts {0, 1}
→ Collect all emails: {a@, b@, c@}
Leader 2: account {2}
→ Collect all emails: {d@}
Elegant grouping! 🎯
Part 5: The Algorithm - Step by Step 📝
✨ Complete algorithm:
STEP 1: Setup Union-Find
parent[i] = i for each account i
STEP 2: Build email → account mapping
For each account i:
For each email in account i:
emailToAccounts[email].add(i)
STEP 3: Union accounts sharing emails
For each email:
accounts = emailToAccounts[email]
If multiple accounts have this email:
Union them all together!
STEP 4: Group accounts by leader
For each account i:
leader = find(i)
componentsEmails[leader].addAll(emails from account i)
STEP 5: Build result
For each leader:
Create merged account:
[name, email1, email2, ...] (sorted)
Clean and efficient! 🌟
Part 6: Why Union-Find Wins! 💪
💭 Comparing approaches:
DFS/BFS:
✅ Intuitive (connected components)
❌ Need to build graph first
❌ O(E) graph building + O(V+E) traversal
Union-Find:
✅ Direct grouping (no graph needed!)
✅ O(α(n)) per operation
✅ Natural for merging
✅ Elegant code
For this problem:
Union-Find is perfect! 🎯
Why? We're not exploring paths...
We're just grouping things together!
Union-Find = made for this! 🔗
🎯 Approach 1: Union-Find (Optimal!)
The Key Insight 💡
Use Union-Find to group accounts by shared emails 🔗! Build map: email → accounts having it 📧. Union all accounts sharing same email 🤝. Group emails by account leader 👑. Build result from groups! 📊 Time: O(N × K × α(n)) where N = accounts, K = emails per account ⚡, Space: O(N × K) 📦!
Perfect for merging/grouping! 🌟
Complete Implementation
class Solution {
// ═══════════════════════════════════════════════════════
// UNION-FIND FOR ACCOUNT GROUPING 🔗
// ═══════════════════════════════════════════════════════
private int[] parent;
private int[] rank;
public List<List<String>> accountsMerge(List<List<String>> accounts) {
// ═══════════════════════════════════════════════════
// UNION-FIND APPROACH 🌟
// ═══════════════════════════════════════════════════
// 1. Map emails to accounts
// 2. Union accounts with shared emails
// 3. Group emails by account leader
// 4. Build merged result
// ═══════════════════════════════════════════════════
int n = accounts.size();
parent = new int[n];
rank = new int[n];
// ═══════════════════════════════════════════════════
// INITIALIZE UNION-FIND 🏗️
// ═══════════════════════════════════════════════════
for (int i = 0; i < n; i++) {
parent[i] = i;
}
// ═══════════════════════════════════════════════════
// STEP 1: BUILD EMAIL → ACCOUNT MAPPING 📧
// ═══════════════════════════════════════════════════
// Key: email
// Value: account index that has this email
// ═══════════════════════════════════════════════════
Map<String, Integer> emailToAccount = new HashMap<>();
for (int i = 0; i < n; i++) {
List<String> account = accounts.get(i);
// Skip name (index 0), process emails (index 1+)
for (int j = 1; j < account.size(); j++) {
String email = account.get(j);
if (emailToAccount.containsKey(email)) {
// ───────────────────────────────────────
// EMAIL ALREADY SEEN! 🔍
// Union current account with previous account
// ───────────────────────────────────────
int prevAccount = emailToAccount.get(email);
union(i, prevAccount);
} else {
// ───────────────────────────────────────
// FIRST TIME SEEING THIS EMAIL 🆕
// Map it to current account
// ───────────────────────────────────────
emailToAccount.put(email, i);
}
}
}
// ═══════════════════════════════════════════════════
// STEP 2: GROUP EMAILS BY ACCOUNT LEADER 👑
// ═══════════════════════════════════════════════════
// Key: leader account index
// Value: set of all emails for this group
// ═══════════════════════════════════════════════════
Map<Integer, Set<String>> components = new HashMap<>();
for (int i = 0; i < n; i++) {
int leader = find(i);
// Get or create email set for this leader
components.putIfAbsent(leader, new TreeSet<>());
// Add all emails from current account (skip name at index 0)
List<String> account = accounts.get(i);
for (int j = 1; j < account.size(); j++) {
components.get(leader).add(account.get(j));
}
}
// ═══════════════════════════════════════════════════
// STEP 3: BUILD RESULT 📋
// ═══════════════════════════════════════════════════
List<List<String>> result = new ArrayList<>();
for (int leader : components.keySet()) {
List<String> mergedAccount = new ArrayList<>();
// Add name (from leader account)
String name = accounts.get(leader).get(0);
mergedAccount.add(name);
// Add sorted emails (TreeSet already sorted!)
mergedAccount.addAll(components.get(leader));
result.add(mergedAccount);
}
return result;
}
// ═══════════════════════════════════════════════════════
// FIND: Get leader with path compression ⚡
// ═══════════════════════════════════════════════════════
private int find(int x) {
if (parent[x] != x) {
parent[x] = find(parent[x]);
}
return parent[x];
}
// ═══════════════════════════════════════════════════════
// UNION: Merge two accounts by rank 📊
// ═══════════════════════════════════════════════════════
private void union(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX == rootY) return;
if (rank[rootX] < rank[rootY]) {
parent[rootX] = rootY;
} else if (rank[rootX] > rank[rootY]) {
parent[rootY] = rootX;
} else {
parent[rootY] = rootX;
rank[rootX]++;
}
}
}
Why This Works 🎯
Email → Account mapping enables unions: 📧 - First time seeing email: map to account - Second time: union current with previous! - Transitive: if A↔B and B↔C, then A↔C!
TreeSet for automatic sorting: 📊 - Emails need to be sorted in output - TreeSet sorts as we add! - No final sort needed!
Group by leader collects all emails: 👑 - Accounts with same leader = same person - Collect all emails from all accounts in group - TreeSet deduplicates automatically!
🔍 Detailed Dry Run - Union-Find Approach
Let's trace on:
accounts = [
["John","j1@mail.com","j2@mail.com"],
["John","j2@mail.com","j3@mail.com"],
["Mary","m1@mail.com"]
]
╔══════════════════════════════════════════════════════════════╗
║ INITIALIZATION ║
╚══════════════════════════════════════════════════════════════╝
n = 3 accounts (indices 0, 1, 2)
Initialize Union-Find:
parent = [0, 1, 2] // Each account is own parent
rank = [0, 0, 0]
Sets: {0}, {1}, {2} (all separate)
╔══════════════════════════════════════════════════════════════╗
║ STEP 1: BUILD EMAIL → ACCOUNT MAPPING ║
╚══════════════════════════════════════════════════════════════╝
╔═════════════════════════════════════════════════╗
║ PROCESS ACCOUNT 0: ["John","j1@","j2@"] ║
╠═════════════════════════════════════════════════╣
║ ║
║ Email "j1@": ║
║ First time seeing this email! ║
║ emailToAccount["j1@"] = 0 ║
║ ║
║ Email "j2@": ║
║ First time seeing this email! ║
║ emailToAccount["j2@"] = 0 ║
║ ║
║ State: ║
║ emailToAccount = { ║
║ "j1@" → 0, ║
║ "j2@" → 0 ║
║ } ║
╚═════════════════════════════════════════════════╝
╔═════════════════════════════════════════════════╗
║ PROCESS ACCOUNT 1: ["John","j2@","j3@"] ║
╠═════════════════════════════════════════════════╣
║ ║
║ Email "j2@": ║
║ Already seen! (mapped to account 0) 🔍 ║
║ This means accounts 0 and 1 share "j2@"! ║
║ ║
║ UNION(1, 0): ║
║ rootX = find(1) = 1 ║
║ rootY = find(0) = 0 ║
║ rank[1] == rank[0] (both 0) ║
║ parent[0] = 1 ║
║ rank[1]++ → 1 ║
║ ║
║ Result: ║
║ parent = [1, 1, 2] ║
║ rank = [0, 1, 0] ║
║ ║
║ Visual: ║
║ 1 ║
║ ↑ ║
║ 0 2 ║
║ ║
║ Sets: {0, 1}, {2} ║
║ ║
║ Email "j3@": ║
║ First time seeing this email! ║
║ emailToAccount["j3@"] = 1 ║
║ ║
║ State: ║
║ emailToAccount = { ║
║ "j1@" → 0, ║
║ "j2@" → 0, ║
║ "j3@" → 1 ║
║ } ║
╚═════════════════════════════════════════════════╝
╔═════════════════════════════════════════════════╗
║ PROCESS ACCOUNT 2: ["Mary","m1@"] ║
╠═════════════════════════════════════════════════╣
║ ║
║ Email "m1@": ║
║ First time seeing this email! ║
║ emailToAccount["m1@"] = 2 ║
║ ║
║ Final Union-Find state: ║
║ parent = [1, 1, 2] ║
║ ║
║ Sets: {0, 1}, {2} ║
╚═════════════════════════════════════════════════╝
╔══════════════════════════════════════════════════════════════╗
║ STEP 2: GROUP EMAILS BY LEADER ║
╚══════════════════════════════════════════════════════════════╝
For each account, find its leader and collect emails:
╔═════════════════════════════════════════════════╗
║ ACCOUNT 0: ["John","j1@","j2@"] ║
╠═════════════════════════════════════════════════╣
║ Leader: find(0) = 1 ║
║ Add emails to components[1]: ║
║ components[1] = {"j1@", "j2@"} ║
╚═════════════════════════════════════════════════╝
╔═════════════════════════════════════════════════╗
║ ACCOUNT 1: ["John","j2@","j3@"] ║
╠═════════════════════════════════════════════════╣
║ Leader: find(1) = 1 ║
║ Add emails to components[1]: ║
║ components[1] = {"j1@", "j2@", "j3@"} ║
║ (TreeSet deduplicates "j2@") ║
╚═════════════════════════════════════════════════╝
╔═════════════════════════════════════════════════╗
║ ACCOUNT 2: ["Mary","m1@"] ║
╠═════════════════════════════════════════════════╣
║ Leader: find(2) = 2 ║
║ Add emails to components[2]: ║
║ components[2] = {"m1@"} ║
╚═════════════════════════════════════════════════╝
Final components map:
components = {
1 → {"j1@", "j2@", "j3@"}, // TreeSet sorted
2 → {"m1@"}
}
╔══════════════════════════════════════════════════════════════╗
║ STEP 3: BUILD RESULT ║
╚══════════════════════════════════════════════════════════════╝
For each leader, create merged account:
╔═════════════════════════════════════════════════╗
║ LEADER 1 (accounts 0 and 1 merged) ║
╠═════════════════════════════════════════════════╣
║ Name: accounts[1][0] = "John" ║
║ Emails: {"j1@", "j2@", "j3@"} ║
║ ║
║ Merged account: ║
║ ["John", "j1@", "j2@", "j3@"] ║
╚═════════════════════════════════════════════════╝
╔═════════════════════════════════════════════════╗
║ LEADER 2 (account 2 alone) ║
╠═════════════════════════════════════════════════╣
║ Name: accounts[2][0] = "Mary" ║
║ Emails: {"m1@"} ║
║ ║
║ Account: ║
║ ["Mary", "m1@"] ║
╚═════════════════════════════════════════════════╝
╔══════════════════════════════════════════════════════════════╗
║ FINAL RESULT ║
╠══════════════════════════════════════════════════════════════╣
║ ║
║ [ ║
║ ["John", "j1@mail.com", "j2@mail.com", "j3@mail.com"], ║
║ ["Mary", "m1@mail.com"] ║
║ ] ║
║ ║
║ First two John accounts merged (shared j2@)! ✅ ║
║ Mary account separate! ✅ ║
║ All emails sorted within each account! ✅ ║
║ ║
╚══════════════════════════════════════════════════════════════╝
🔑 Key Observations:
-
Email mapping triggers unions: 📧 When "j2@" seen second time, union accounts 0 and 1
-
Transitive grouping works: 🔗 Even if account 0 and 1 don't directly list all same emails, they're merged
-
TreeSet handles deduplication: 📊 "j2@" appears in both accounts but only once in result
-
Leader determines group: 👑 Both accounts 0 and 1 have leader 1, so grouped together
-
Sorting automatic: ✨ TreeSet keeps emails sorted as we add them!
📊 Complexity Analysis
Union-Find Approach
Time Complexity: O(N × K × α(n) + N × K log K) ⚡
Where:
N = number of accounts
K = average emails per account
α(n) = inverse Ackermann (≈ O(1))
Breakdown:
Build email mapping: O(N × K)
For each account: O(K)
Union operation: O(α(n)) ≈ O(1)
Total: O(N × K × α(n))
Group by leader: O(N × K)
For each account: O(K)
TreeSet add: O(log K)
Total: O(N × K log K)
Build result: O(N × K)
Already sorted by TreeSet
Total: O(N × K × α(n) + N × K log K)
Dominated by sorting: O(N × K log K)
Example: N = 1000, K = 5
Operations: ~50,000 (very fast!) ✅
Space Complexity: O(N × K) 📦
Union-Find arrays: O(N)
Email mapping: O(N × K) unique emails
Components map: O(N × K) all emails stored
Result: O(N × K)
Total: O(N × K) 📦
📝 Quick Revision Notes
🎯 Core Concept
Accounts Merge groups accounts by shared emails using Union-Find 🔗! Map email → first account seeing it 📧. When email seen again, union current with first 🤝! Group emails by account leader (find(i)) 👑. Use TreeSet for sorted, deduplicated emails 📊! Collect all emails for each leader group 📝. Time: O(N×K log K) ⚡, Space: O(N×K) 📦! Beautiful grouping pattern! 🌟
⚡ Quick Implementation
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
public class Solution {
public List<List<String>> accountsMerge(List<List<String>> accounts) {
List<List<String>> res = new ArrayList<>();
int n = accounts.size();
// Step 1: Initialize parent and rank arrays.
int[] parent = new int[n];
int[] rank = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i; // keep parent itself for now.
rank[i] = 0;
}
// Step 2: Find leaders of all accounts
// create a mapping of email and account IDs
// If any 2 emails share same account ID, merge them to the common account.
HashMap<String, Integer> emailAccounts = new HashMap<>();
for (int i = 0; i < n; i++) {
for (int j = 1; j < accounts.get(i).size(); j++) {
String email = accounts.get(i).get(j);
// check if email is already mapped to some account ID already
// If yes, map to the common leader.
if (emailAccounts.containsKey(email)) {
int prev = emailAccounts.get(email); // prev account
union(prev, i, parent, rank);
} else {
emailAccounts.put(email, i);
}
}
}
// System.out.println(Arrays.toString(parent)); // [0, 0, 2, 3]
// System.out.println(Arrays.toString(rank)); // [1, 0, 0, 0]
// Step 3: create a map of account leader and emails
// Traverse through all accounts and find leader of each account.
// Now, add emails of all those accounts to that leader
Map<Integer, Set<String>> leadersEmails = new HashMap<>();
for (int i = 0; i < n; i++) {
// Getting leader of each account
int leader = find(i, parent);
leadersEmails.putIfAbsent(leader, new TreeSet<>());
for (int j = 1; j < accounts.get(i).size(); j++) {
leadersEmails.get(leader).add(accounts.get(i).get(j));
}
// {0=[john00@mail.com, john_newyork@mail.com, johnsmith@mail.com],
// 2=[mary@mail.com], 3=[johnnybravo@mail.com]}
// System.out.println(leadersEmails);
}
// Step 4: create the final result
for (int leader : leadersEmails.keySet()) {
List<String> temp = new ArrayList<>();
temp.add(accounts.get(leader).get(0));
temp.addAll(leadersEmails.get(leader));
res.add(new ArrayList<>(temp));
}
return res;
}
private void union(int prev, int curr, int[] parent, int[] rank) {
int rootPrev = find(prev, parent);
int rootCurr = find(curr, parent);
// no action if they already share a common leader.
if (rootCurr == rootPrev) {
return;
}
if (rank[rootPrev] < rank[rootCurr]) {
parent[rootPrev] = rootCurr;
} else if (rank[rootPrev] > rank[rootCurr]) {
parent[rootCurr] = rootPrev;
} else {
parent[rootCurr] = rootPrev;
rank[rootPrev]++;
}
}
private int find(int i, int[] parent) {
if (parent[i] != i) {
parent[i] = find(parent[i], parent);
}
return parent[i];
}
public static void main(String[] args) {
Solution s = new Solution();
System.out.println(s.accountsMerge(List.of(List.of("John", "johnsmith@mail.com", "john_newyork@mail.com"),
List.of("John", "johnsmith@mail.com", "john00@mail.com"),
List.of("Mary", "mary@mail.com"),
List.of("John", "johnnybravo@mail.com"))));
}
}
🎪 Memory Aid
"Email is the key" 🔑
"First time map, second time unite" 🤝
"Group by leader, make it complete" 👑
"TreeSet sorts, result is neat!" 📊 ✨
⚠️ Don't Forget
- Skip name at index 0 - only process emails (index 1+)! 📧
- Map email → account - enables union detection! 🔗
- Use TreeSet - automatic sorting and deduplication! 📊
- Group by leader - find(i) not i! 👑
- Add name to result - from leader's account! 📝
- Path compression - for fast finds! ⚡
🎉 Congratulations!
You've mastered Union-Find Grouping Pattern! 🌟
What You Learned:
✅ Grouping by shared properties - common attribute connects items! 🔗
✅ Email → Account mapping - detect shared properties efficiently! 📧
✅ Transitive merging - if A~B and B~C, then A~C! 🌊
✅ Leader-based grouping - collect items by their root! 👑
✅ TreeSet for sorting - automatic sorting during collection! 📊
The Beautiful Pattern:
Union-Find Grouping Pattern:
1. Map property → item
When property seen again → union items!
2. Group items by leader
Same leader = same group
3. Collect properties for each group
Use TreeSet for automatic sorting
This pattern solves:
✅ Account merging
✅ Friend grouping
✅ Cluster detection
✅ Item deduplication
✅ All transitive grouping problems!
Three Union-Find patterns now:
1. Connected components (Problem 195) 🔢
2. Cycle detection (Problem 198) 🔄
3. Grouping by property (Problem 199) 🔗
Union-Find mastery unlocked! 🔑
Twenty-two problems completed! 🚀
Union-Find is your Swiss Army knife for connectivity! 💪✨🎯🔗