An interactive visualization of the Union-Find approach to merging accounts. Watch how shared emails connect accounts into groups, then see the merged results.
Union-Find LeetCode 721 ↗ Mediumaccounts where each element accounts[i] is a list of strings,
the first element accounts[i][0] is a name, and the rest are emails
representing emails of the account.fun accountsMerge(accounts: List<List<String>>): List<List<String>> { val parent = mutableMapOf<String, String>() fun find(x: String): String { var node = x while (parent[node] != node) { parent[node] = parent[parent[node]!!]!! node = parent[node]!! } return node } fun union(x: String, y: String) { val px = find(x); val py = find(y) if (px != py) parent[px] = py } val emailToName = mutableMapOf<String, String>() for (account in accounts) { val name = account[0]; val emails = account.drop(1) for (email in emails) { if (email !in parent) parent[email] = email emailToName[email] = name union(emails[0], email) } } val groups = mutableMapOf<String, MutableList<String>>() for (email in parent.keys) { val root = find(email) groups.getOrPut(root) { mutableListOf() }.add(email) } return groups.map { (root, emails) -> listOf(emailToName[root]!!) + emails.sorted() } }
Two accounts belong to the same person if they share any email address. This is a connectivity problem -- we need to find connected components where edges are defined by shared emails. Union-Find (Disjoint Set Union) is the perfect data structure for this.
parent dictionary for Union-Find and an email_to_name mapping.union(emails[0], email) -- union the first email of this account with every other email in the account.[name] + sorted(emails).The key trick is that within each account, we union all emails with the first email. If two accounts share an email, that shared email links both accounts' first emails together transitively.
Where N is the number of accounts and K is the maximum number of emails per account.
The alpha factor is the inverse Ackermann function from Union-Find with path compression,
which is effectively O(1) for all practical inputs. Sorting the final groups adds O(NK log(NK)).