写在前面的话:
题目链接:(https://leetcode-cn.com/problems/accounts-merge/),
这道题综合性极强,搞懂这一题,关于图方面的题就都会啦!大家一定要认真搞懂,涉及了图的深度遍历,宽度遍历,并查集..
题目描述:
给定一个列表 accounts
,每个元素 accounts[i]
是一个字符串列表,其中第一个元素 accounts[i][0]
是 名称 (name),其余元素是 emails 表示该帐户的邮箱地址。
现在,我们想合并这些帐户。如果两个帐户都有一些共同的邮件地址,则两个帐户必定属于同一个人。请注意,即使两个帐户具有相同的名称,它们也可能属于不同的人,因为人们可能具有相同的名称。一个人最初可以拥有任意数量的帐户,但其所有帐户都具有相同的名称。
合并帐户后,按以下格式返回帐户:每个帐户的第一个元素是名称,其余元素是按顺序排列的邮箱地址。accounts 本身可以以任意顺序返回。
示例:
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]
的范围内。
思路:
首先,我们理解题意,有个列表,列表中每一个元素由人名和邮箱组成.有重名的可能是一个人,也可能不是一个人.我们通过他们是否有相同邮箱来判定到底是不是同一个人.目标:是缩减这个列表,使同一个人邮箱(按字典顺序)合并在一起.
接下来我们考虑方法,其实最容易的想的就是集合操作,就是说两个同名的邮箱有重叠部分说明就是同一人,我们合并起来.
另一种思考就是,用图.我们把同一个人的邮箱连起来,找一个人所有的邮箱,就是找到他的连通分量.所有,这里就有3种方法,dfs,bfs,并查集.
代码:
集合
def accountsMerge(self, accounts):
from collections import defaultdict
if not accounts:
return
lookup = defaultdict(list)
res = []
for account in accounts:
name = account[0]
email = set(account[1:])
lookup[name].append(email)
for e in lookup[name][:-1]:
if e & email:
lookup[name].remove(e)
lookup[name][-1].update(e)
for key, val in lookup.items():
for tmp in val:
res.append([key] + list(sorted(tmp)))
return res
dfs
def accountsMerge(self, accounts):
from collections import defaultdict, deque
graph = defaultdict(set)
email_to_name = defaultdict()
for account in accounts:
name = account[0]
emails = account[1:]
for email in emails:
email_to_name[email] = name
graph[emails[0]].add(email)
graph[email].add(emails[0])
# print(graph)
visited = set()
res = []
def dfs(e):
new_list.append(e)
for t in graph[e]:
if t not in visited:
visited.add(t)
dfs(t)
for e in graph:
if e not in visited:
visited.add(e)
new_list = []
dfs(e)
res.append([email_to_name[e]] + sorted(new_list))
return res
bfs
def accountsMerge(self, accounts):
from collections import defaultdict, deque
graph = defaultdict(set)
email_to_name = defaultdict()
for account in accounts:
name = account[0]
emails = account[1:]
for email in emails:
email_to_name[email] = name
graph[emails[0]].add(email)
graph[email].add(emails[0])
# print(graph)
visited = set()
res = []
def bfs(e):
ans = []
q = deque()
q.appendleft(e)
while q:
tmp = q.pop()
ans.append(tmp)
for t in graph[tmp]:
if t not in visited:
visited.add(t)
q.appendleft(t)
return ans
for e in graph:
# print(e)
if e not in visited:
visited.add(e)
ans = bfs(e)
res.append([email_to_name[e]] + sorted(ans))
return res
并查集
def accountsMerge(self, accounts):
from collections import defaultdict
f = {}
def find(x):
f.setdefault(x, x)
if x!= f[x]:
f[x] = find(f[x])
return f[x]
def union(x, y):
f[find(x)] = find(y)
lookup = {}
n = len(accounts)
for idx, account in enumerate(accounts):
name = account[0]
email = account[1:]
for e in email:
if e in lookup:
union(idx, lookup[e])
else:
lookup[e] = idx
# print(f)
disjointSet = defaultdict(set)
for i in range(n):
tmp = find(i)
for es in accounts[i][1:]:
disjointSet[tmp].add(es)
# print(disjointSet)
res = []
for key, val in disjointSet.items():
res.append([accounts[key][0]] + list(sorted(val)))
return res