题目
难度:★★★★☆
类型:图
方法:并查集
力扣链接请移步本题传送门
更多力扣中等题的解决方案请移步力扣中等题目录
给定一个列表 accounts,每个元素 accounts[i] 是一个字符串列表,其中第一个元素 accounts[i][0] 是 名称 (name),其余元素是 emails 表示该帐户的邮箱地址。
现在,我们想合并这些帐户。如果两个帐户都有一些共同的邮件地址,则两个帐户必定属于同一个人。请注意,即使两个帐户具有相同的名称,它们也可能属于不同的人,因为人们可能具有相同的名称。一个人最初可以拥有任意数量的帐户,但其所有帐户都具有相同的名称。
合并帐户后,按以下格式返回帐户:每个帐户的第一个元素是名称,其余元素是按顺序排列的邮箱地址。accounts 本身可以以任意顺序返回。
例子 1:
Input:
accounts = [["John", "johnsmith@mail.com", "john00@mail.com"], ["John", "johnnybravo@mail.com"], ["John", "johnsmith@mail.com", "john_newyork@mail.com"], ["Mary", "mary@mail.com"]]
Output: [["John", 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com'], ["John", "johnnybravo@mail.com"], ["Mary", "mary@mail.com"]]
Explanation:
第一个和第三个 John 是同一个人,因为他们有共同的电子邮件 "johnsmith@mail.com"。
第二个 John 和 Mary 是不同的人,因为他们的电子邮件地址没有被其他帐户使用。
我们可以以任何顺序返回这些列表,例如答案[['Mary','mary@mail.com'],['John','johnnybravo@mail.com'],
['John','john00@mail.com','john_newyork@mail.com','johnsmith@mail.com']]仍然会被接受。
注意:
accounts的长度将在[1,1000]的范围内。
accounts[i]的长度将在[1,10]的范围内。
accounts[i][j]的长度将在[1,30]的范围内。
解答
这道题是一个典型的图的问题,我们使用并查集来做。这里比较推荐的参考资料是某大佬的并查集总结
定义邮件的并查集UF(Union Find的缩写),包含寻找根节点(find)和合并(union)两个方法,UF包含一个字典,或者说哈希表,记录了父节点与子节点的联系关系。实际上,我们把并查集的树形结构中的每一条边都用哈希对应的方式来表达,这样,一棵树就在字典里了。
我们在构建并查集时,为了避免后续使用时查找失败,就先存放好所有邮件对应的父节点,即自己。
为了获得邮件与用户的归属关系,我们建立一个哈希表,email_to_use。
此外,对于每一个邮件列表,我们都让它们通过合并,使得在并查集的同一个连通域内。因此,造成的结果是,只要两个邮件列表中有一样的邮件地址,那么这两个列表中所有的邮件地址,都在同一个联通域内,或者说,它们有一样的根节点,这个根节点可以通过find方法找到。
只要找到了连通域,从中选择任意一个邮件,就可以通过邮件-用户对应表寻找到用户,也就可以完成邮件的合并了。
import collections
class UF:
def __init__(self, accounts):
self.parent = dict()
for account in accounts:
for item in account[1:]:
self.parent[item] = item
def find(self, x):
while x != self.parent[x]:
x = self.parent[x]
return x
def union(self, p, q):
self.parent[self.find(p)] = self.find(q)
class Solution:
def accountsMerge(self, accounts):
uf = UF(accounts)
email_to_name = {}
# 1. 构建邮件地址-姓名映射表,2. 建立邮件地址的并查集
for account in accounts:
user, addresses = account[0], account[1:]
for i, address in enumerate(addresses):
email_to_name[address] = user
if i < len(addresses) - 1:
uf.union(address, addresses[i+1])
# 归并并查集中的联通区
root_to_emails = collections.defaultdict(list)
for email in email_to_name.keys():
root_to_emails[uf.find(email)].append(email)
res = [[email_to_name[value[0]]] + sorted(value) for value in root_to_emails.values()]
return res
如有疑问或建议,欢迎评论区留言~
有关更多力扣中等题的python解决方案,请移步力扣中等题解析