目录
解题思路
本题的解题思想主要是利用并查集(Union Find)数据结构来合并具有相同账户名称的邮箱地址。首先,我们遍历所有账户信息,建立邮箱地址到唯一索引的映射(emailToIndex
)和邮箱地址到账户名称的映射(emailToName
)。这样做是为了能够快速地在后续步骤中查找邮箱的索引和对应的账户名称。
然后,我们使用并查集来跟踪哪些邮箱地址应该被合并到同一个账户中。对于每个账户,我们将其中的第一个邮箱地址作为代表,并将其与账户中的其他邮箱地址进行合并操作。这样,属于同一账户的邮箱地址在并查集中就会有相同的根节点。
最后,我们遍历所有邮箱地址,根据并查集的结果将它们分组到对应的账户中。对于每个组(即每个并查集的根节点),我们收集其下所有的邮箱地址,并从任意一个邮箱地址中获取对应的账户名称(因为我们已经假设了每个账户内的邮箱地址都关联到同一个名称)。然后,我们将账户名称和收集到的邮箱地址列表添加到结果列表中。
解题过程
-
初始化映射和计数器:创建
emailToIndex
和emailToName
映射,以及一个计数器emailsCount
来跟踪邮箱地址的数量。 -
建立映射:遍历
accounts
列表,为每个邮箱地址分配一个唯一索引,并将其与账户名称关联起来。 -
构建并查集:创建
UnionFind
对象,并根据emailToIndex
中的索引进行邮箱地址的合并操作。 -
分组邮箱地址:遍历所有邮箱地址,根据并查集的结果将它们分组到对应的账户中。
-
构建结果列表:对于每个分组,收集其下的邮箱地址,并从任意一个邮箱地址中获取账户名称,然后构建并添加到结果列表中。
时间复杂度
- 遍历
accounts
列表建立映射的时间复杂度可以简化为 O(n),其中 n 是邮箱地址的总数(即 E)。 - 构建并查集和进行合并操作的时间复杂度也是 O(n),因为每个邮箱地址最多被合并一次,并且
union
操作在并查集中通常是高效的。 - 分组邮箱地址和构建结果列表的时间复杂度同样是 O(n),因为我们需要遍历所有邮箱地址一次。
因此,总的时间复杂度可以表示为 O(n),其中 n 是邮箱地址的总数。
空间复杂度
emailToIndex
、emailToName
映射和并查集都需要存储与邮箱地址数量成比例的信息,因此它们的空间复杂度都是 O(n)。- 结果列表的空间复杂度也是 O(n),因为每个邮箱地址在结果列表中只会出现一次(尽管它们被分组到了不同的账户中)。
因此,总的空间复杂度同样可以表示为 O(n),其中 n 是邮箱地址的总数。
Code
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;
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++);
emailToName.put(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);
uf.union(firstIndex, nextIndex);
}
}
Map<Integer, List<String>> indexToEmails = new HashMap<Integer, List<String>>();
for (String email : emailToIndex.keySet()) {
int index = uf.find(emailToIndex.get(email));
List<String> account = indexToEmails.getOrDefault(index, new ArrayList<String>());
account.add(email);
indexToEmails.put(index, account);
}
List<List<String>> merged = new ArrayList<List<String>>();
for (List<String> emails : indexToEmails.values()) {
Collections.sort(emails);
String name = emailToName.get(emails.get(0));
List<String> account = new ArrayList<String>();
account.add(name);
account.addAll(emails);
merged.add(account);
}
return merged;
}
}
class UnionFind {
private int[] parent;
public UnionFind(int size) {
parent = new int[size];
for (int i = 0; i < size; i++) {
parent[i] = i;
}
}
public int find(int x) {
if (parent[x] != x) {
parent[x] = find(parent[x]); // 路径压缩
}
return parent[x];
}
public void union(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX != rootY) {
parent[rootX] = rootY;
}
}
}