721. 账户合并
2024-10-28 08:52:07 # LeetCode

给定一个列表 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“]]

提示

邮箱相同,用户就相同

Solution 1

第一次尝试,通过深度优先搜索解答

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
class Solution {
public List<List<String>> accountsMerge(List<List<String>> accounts) {
Set<String> existEmails = new HashSet<>();
Set<Integer> existIdxs = new HashSet<>();
List<List<String>> merged = new ArrayList<>();
List<String> temp;

for (List<String> ignored : accounts) {
temp = new ArrayList<>();
dfs(existEmails, existIdxs, temp, accounts);
if (!temp.isEmpty()) {
if (temp.size() > 2) {
Set<String> emailSet = new HashSet<>(temp.subList(1, temp.size()));
List<String> emails = new ArrayList<>(emailSet);
Collections.sort(emails);
List<String> list = new ArrayList<>();
list.add(temp.get(0));
list.addAll(emails);
merged.add(list);
} else {
merged.add(temp);
}
}
}

return merged;
}

private void dfs(Set<String> existEmails,
Set<Integer> existIdxs,
List<String> temp,
List<List<String>> accounts) {
for (int i = 0, k = accounts.size(); i < k; i++) {
if (!existIdxs.contains(i)) {
List<String> emails = accounts.get(i).subList(1, accounts.get(i).size());
if (temp.isEmpty()) {
temp.addAll(accounts.get(i));
existIdxs.add(i);
existEmails.addAll(emails);
} else {
boolean merged = false;
for (String email : emails) {
if (existEmails.contains(email)) {
merged = true;
break;
}
}
if (merged) {
existIdxs.add(i);
existEmails.addAll(emails);
temp.addAll(emails);
dfs(existEmails, existIdxs, temp, accounts);
}
}
}
}
}
}
  • 执行用时:469 ms(击败 6.28% 使用 Java 的用户)
  • 内存消耗:50.55 MB(击败 5.00% 使用 Java 的用户)

Solution 2

推荐解法,通过 并查集 解答

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
class Solution {
public List<List<String>> accountsMerge(List<List<String>> accounts) {
Map<String, Integer> email2Index = new HashMap<>();
Map<String, String> email2Name = new HashMap<>();

int emailIdx = 0;
int count = 0;
for (List<String> account : accounts) {
String name = account.get(0);
count = account.size();
for (int i = 1; i < count; i++) {
String email = account.get(i);
if (!email2Index.containsKey(email)) {
email2Index.put(email, emailIdx++);
email2Name.put(email, name);
}
}
}

UnionFind uf = new UnionFind(emailIdx);
for (List<String> account : accounts) {
String firstEmail = account.get(1);
int firstIdx = email2Index.get(firstEmail);
for (int i = 2, k = account.size(); i < k; i++) {
String nextEmail = account.get(i);
int nextIdx = email2Index.get(nextEmail);
uf.union(firstIdx, nextIdx);
}
}

Map<Integer, List<String>> emailIdxMap = new HashMap<>();
email2Index.forEach((k, v) -> {
int rootIdx = uf.find(v);
List<String> emails = emailIdxMap.getOrDefault(rootIdx, new ArrayList<>());
emails.add(k);
emailIdxMap.put(rootIdx, emails);
});

List<List<String>> merged = new ArrayList<>();
List<String> val;
for (List<String> emails : emailIdxMap.values()) {
Collections.sort(emails);
String name = email2Name.get(emails.get(0));
val = new ArrayList<>();
val.add(name);
val.addAll(emails);
merged.add(val);
}

return merged;
}

private static class UnionFind {

int[] node;

public UnionFind(int size) {
node = new int[size];
for (int i = 0; i < size; i++) {
node[i] = i;
}
}

public void union(int index1, int index2) {
node[find(index2)] = find(index1);
}

public int find(int index) {
if (node[index] != index) {
node[index] = find(node[index]);
}
return node[index];
}
}
}
  • 执行用时:28 ms(击败 94.76% 使用 Java 的用户)
  • 内存消耗:47.93 MB(击败 32.94% 使用 Java 的用户)

Source

721. 账户合并 - 力扣(LeetCode)