题目描述
给定一个列表 accounts,每个元素 accounts[i] 是一个字符串列表,其中第一个元素 accounts[i][0] 是 名称 (name),其余元素是 emails 表示该帐户的邮箱地址。
现在,我们想合并这些帐户。如果两个帐户都有一些共同的邮件地址,则两个帐户必定属于同一个人。请注意,即使两个帐户具有相同的名称,它们也可能属于不同的人,因为人们可能具有相同的名称。一个人最初可以拥有任意数量的帐户,但其所有帐户都具有相同的名称。
合并帐户后,按以下格式返回帐户:每个帐户的第一个元素是名称,其余元素是按顺序排列的邮箱地址。accounts 本身可以以任意顺序返回。
力扣:721 账户合并
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 是不同的人,因为他们的电子邮件地址没有被其他帐户使用。
题目分析
题目要求很明确,通过具有相同的邮件,确认两个相同姓名的用户是否是同一个用户,是则返回该用户的所有邮箱的合集。
1、构建Map ,保存邮箱到姓名的关系,但因为姓名是存在重复的,所以不能以姓名作为并查集的根,这里想到了采用 accounts 的下标。
Map<String , Integer> parent = new HashMap<>();
2、此外,还需要构建一个中间结果Map , 保存下标和邮箱的映射,此时的邮箱是去重后的(该下标代表的用户,所拥有的的所有邮箱),采用 TreeSet 进行去重处理。
Map<Integer, TreeSet<String>> temp = new HashMap<>();
3、从中间结果 temp 中还原出 List<List> ,通过下标获取 accounts 的姓名,并将姓名和邮箱添加到同一个 List 得到一个用户和所有的邮箱。
int root = entry.getKey();
String name = accounts.get(root).get(0);
list.add(name);
list.addAll(entry.getValue());
AC代码:
class Solution {
// 查找根
public int find(int[] father , int x){
return father[x] == -1 ? x : find(father , father[x]);
}
// 合并并查集
public void union(int[] father , int x , int y){
int x_parent = find(father , x);
int y_parent = find(father , y);
if(x_parent != y_parent){
father[y_parent] = x_parent;
}
}
public List<List<String>> accountsMerge(List<List<String>> accounts) {
Map<String , Integer> parent = new HashMap<>();
int[] father = new int[accounts.size()];
Arrays.fill(father , -1);
// 构建 姓名和邮箱 之间的并查集
for(int i = 0 ; i < accounts.size(); i ++){
List<String> account = accounts.get(i);
for(int j = 1 ; j < account.size() ; j ++){
String email = account.get(j);
// 如果邮箱已经存在,说明存在集合是属于同一个用户的,将其合并
if(parent.containsKey(email)){
union(father , parent.get(email) , i);
}
// 如果不存在,加入
parent.put(email , i);
}
}
// 将并查集根相同,邮箱合并到一起,得到中间结果 temp
Map<Integer , Set<String>> temp = new HashMap<>();
for(int i = 0 ; i < accounts.size() ; i ++){
int root = find(father , i);
List<String> account = accounts.get(i);
List<String> emails = account.subList(1 , account.size());
// 如果 根 已经出现过,现在只需要将邮箱合并,因为是 Set 无需考虑是否会重复
if(temp.containsKey(root)){
temp.get(root).addAll(emails);
} else {
// 如果没有出现 此根 ,构建一个 Set 集合,将邮箱加入
temp.put(root , new TreeSet<>(emails));
}
}
// 将中间结果 temp 转换为输出的返回类型
List<List<String>> result = new ArrayList<>();
for(Map.Entry<Integer , Set<String>> entry : temp.entrySet()){
List<String> list = new ArrayList<>();
int root = entry.getKey();
String name = accounts.get(root).get(0); // 通过下标获取姓名
list.add(name); // 将姓名添加到 list
list.addAll(entry.getValue()); // 然后将邮箱添加到 list
result.add(list);
}
return result;
}
}