题目
给定一个列表 accounts,每个元素 accounts[i] 是一个字符串列表,其中第一个元素 accounts[i][0] 是 名称 (name),其余元素是 emails 表示该账户的邮箱地址。
现在,我们想合并这些账户。如果两个账户都有一些共同的邮箱地址,则两个账户必定属于同一个人。请注意,即使两个账户具有相同的名称,它们也可能属于不同的人,因为人们可能具有相同的名称。一个人最初可以拥有任意数量的账户,但其所有账户都具有相同的名称。
合并账户后,按以下格式返回账户:每个账户的第一个元素是名称,其余元素是 按字符 ASCII 顺序排列 的邮箱地址。账户本身可以以 任意顺序 返回。
示例 1:
输入:accounts = [["John", "johnsmith@mail.com", "john00@mail.com"], ["John", "johnnybravo@mail.com"], ["John", "johnsmith@mail.com", "john_newyork@mail.com"], ["Mary", "mary@mail.com"]]
输出:[["John", 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com'], ["John", "johnnybravo@mail.com"], ["Mary", "mary@mail.com"]]
解释:
第一个和第三个 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']] 也是正确的。
示例 2:
输入: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"],["Hanzo","Hanzo3@m.co","Hanzo1@m.co","Hanzo0@m.co"],["Fern","Fern5@m.co","Fern1@m.co","Fern0@m.co"]]
输出:[["Ethan","Ethan0@m.co","Ethan4@m.co","Ethan5@m.co"],["Gabe","Gabe0@m.co","Gabe1@m.co","Gabe3@m.co"],["Hanzo","Hanzo0@m.co","Hanzo1@m.co","Hanzo3@m.co"],["Kevin","Kevin0@m.co","Kevin3@m.co","Kevin5@m.co"],["Fern","Fern0@m.co","Fern1@m.co","Fern5@m.co"]]
提示:
1 <= accounts.length <= 1000
2 <= accounts[i].length <= 10
1 <= accounts[i][j].length <= 30
accounts[i][0]
由英文字母组成accounts[i][j] (for j > 0)
是有效的邮箱地址
分析
哈希表,key需要处理一下作区分。
遍历列表,加入到hash表中,进行合并。
一个key,遍历其邮件列表,发现有重复进行账户合并。
否则,创建新的key,加入到map中。
处理排序问题!!!
最后,将map转成List。
哈希表处理数据,并查集
进行三次查找,判断有交集-合并操作!!! 耗时过长!!!
public List<List<String>> accountsMerge(List<List<String>> accounts) {
// 哈希表
Map<String, Set<String>> accountMap = new TreeMap<>();
// 遍历每一个账户
for (List<String> accountList : accounts) {
boolean needAddNew = true;
int index = 0;
// 相同名字可以有1000个,遍历历史的哈希表数据
for (int k = 0; k <= 1000; k++) {
// 已经存在了多个name
String key = accountList.get(0) + "#" + k;
// 第k个相同名字存在
if (accountMap.containsKey(key)) {
boolean needMerge = false;
for (int i = 1; i < accountList.size(); i++) {
if (accountMap.get(key).contains(accountList.get(i))) {
needMerge = true;
break;
}
}
if (needMerge) {
needAddNew = false;
// System.out.println("merge " + key + " and " + accountList.get(0));
for (int i = 1; i < accountList.size(); i++) {
accountMap.get(key).add(accountList.get(i));
}
break;
}
} else {
index = k;
break;
}
}
if (needAddNew) {
String key = accountList.get(0) + "#" + index;
// System.out.println("add new " + key);
// 自定义比较器
Set<String> emails = new TreeSet<>();
for (int i = 1; i < accountList.size(); i++) {
emails.add(accountList.get(i));
}
accountMap.put(key, emails);
}
}
// 第一次检查map里面相同name的是否需要merge
List<String> disableKeys = new ArrayList<>();
for (Map.Entry<String, Set<String>> entry : accountMap.entrySet()) {
if (disableKeys.contains(entry.getKey())) {// 已经merge过了
continue;
}
String name = entry.getKey().split("#")[0];
for (Map.Entry<String, Set<String>> entry1 : accountMap.entrySet()) {
// 前缀一致 且 不相等 则可以 merge
if (entry1.getKey().startsWith(name + "#") && !entry1.getKey().equals(entry.getKey())) {
if (!Collections.disjoint(entry.getValue(), entry1.getValue())) {// 存在交集,则merge
// System.out.println("merge " + entry.getKey() + " and " + entry1.getKey());
entry.getValue().addAll(entry1.getValue());
disableKeys.add(entry1.getKey());// entry1废弃
}
}
}
}
// 第一次检查map里面相同name的是否需要merge
for (Map.Entry<String, Set<String>> entry : accountMap.entrySet()) {
if (disableKeys.contains(entry.getKey())) {// 已经merge过了
continue;
}
String name = entry.getKey().split("#")[0];
for (Map.Entry<String, Set<String>> entry1 : accountMap.entrySet()) {
// 前缀一致 且 不相等 则可以 merge
if (entry1.getKey().startsWith(name + "#") && !entry1.getKey().equals(entry.getKey())) {
if (!Collections.disjoint(entry.getValue(), entry1.getValue())) {// 存在交集,则merge
// System.out.println("merge " + entry.getKey() + " and " + entry1.getKey());
entry.getValue().addAll(entry1.getValue());
disableKeys.add(entry1.getKey());// entry1废弃
}
}
}
}
List<List<String>> ans = new ArrayList<>();
for (Map.Entry<String, Set<String>> entry : accountMap.entrySet()) {
if (disableKeys.contains(entry.getKey())) {
continue;
}
List<String> list = new ArrayList<>();
String name = entry.getKey().split("#")[0];
list.add(name);
list.addAll(entry.getValue());
ans.add(list);
}
return ans;
}
}
哈希表处理数据,并查集完成数据映射合并
class Solution {
public List<List<String>> accountsMerge(List<List<String>> accounts) {
Map<String, Integer> emailToIndex = new HashMap<String, Integer>();
Map<String, String> emailToName = new HashMap<String, String>();
int emailsCount = 0;// 统计全部的email的数量
for (List<String> account : accounts) {
String name = account.get(0);
int size = account.size();
for (int i = 1; i < size; i++) {
String email = account.get(i);
if (!emailToIndex.containsKey(email)) {
emailToIndex.put(email, emailsCount++);// 过滤重复的email
emailToName.put(email, name);// 将email与Name映射, 不用list的email映射到同一个name
}
}
}
UnionFind uf = new UnionFind(emailsCount);
for (List<String> account : accounts) {
String firstEmail = account.get(1);
int firstIndex = emailToIndex.get(firstEmail);
int size = account.size();
for (int i = 2; i < size; i++) {
String nextEmail = account.get(i);
int nextIndex = emailToIndex.get(nextEmail);
// 将两个email合并,因为他们是同一个name list下的邮箱
uf.union(firstIndex, nextIndex);
}
}
// 将 email -> index 映射 转换成 index -> email list映射。相同祖先的归为一组
Map<Integer, List<String>> indexToEmails = new HashMap<>();
for (String email : emailToIndex.keySet()) {
int index = uf.find(emailToIndex.get(email));// 并查集查找祖先
List<String> account = indexToEmails.getOrDefault(index, new ArrayList<>());
account.add(email);
indexToEmails.put(index, account);
}
// 构造答案数据
List<List<String>> merged = new ArrayList<>();
for (List<String> emails : indexToEmails.values()) {
// 对于每个list排序
Collections.sort(emails);
String name = emailToName.get(emails.get(0));// 获取分组的name
List<String> account = new ArrayList<>();
account.add(name);
account.addAll(emails);
merged.add(account);
}
return merged;
}
}
class UnionFind {
int[] parent;
public UnionFind(int n) {
parent = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
}
}
// 链接起来
public void union(int index1, int index2) {
parent[find(index2)] = find(index1);
}
public int find(int index) {
// 如果存在祖先
if (parent[index] != index) {
parent[index] = find(parent[index]);// 递归更新成根祖先
}
return parent[index];
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1056615746@qq.com