|
| 1 | +# [0721. 账户合并](https://leetcode.cn/problems/accounts-merge/) |
| 2 | + |
| 3 | +- 标签:深度优先搜索、广度优先搜索、并查集、数组、哈希表、字符串、排序 |
| 4 | +- 难度:中等 |
| 5 | + |
| 6 | +## 题目链接 |
| 7 | + |
| 8 | +- [0721. 账户合并 - 力扣](https://leetcode.cn/problems/accounts-merge/) |
| 9 | + |
| 10 | +## 题目大意 |
| 11 | + |
| 12 | +**描述**: |
| 13 | + |
| 14 | +给定一个列表 $accounts$,每个元素 $accounts[i]$ 是一个字符串列表,其中第一个元素 $accounts[i][0]$ 是名称($name$),其余元素是 $emails$ 表示该账户的邮箱地址。 |
| 15 | + |
| 16 | +现在,我们想合并这些账户。如果两个账户都有一些共同的邮箱地址,则两个账户必定属于同一个人。请注意,即使两个账户具有相同的名称,它们也可能属于不同的人,因为人们可能具有相同的名称。一个人最初可以拥有任意数量的账户,但其所有账户都具有相同的名称。 |
| 17 | + |
| 18 | +**要求**: |
| 19 | + |
| 20 | +合并账户后,按以下格式返回账户:每个账户的第一个元素是名称,其余元素是按字符 ASCII 顺序排列的邮箱地址。账户本身可以以任意顺序返回。 |
| 21 | + |
| 22 | +**说明**: |
| 23 | + |
| 24 | +- $1 \le accounts.length \le 10^{3}$。 |
| 25 | +- $2 \le accounts[i].length \le 10$。 |
| 26 | +- $1 \le accounts[i][j].length \le 30$。 |
| 27 | +- $accounts[i][0]$ 由英文字母组成。 |
| 28 | +- $accounts[i][j] (for j \gt 0)$ 是有效的邮箱地址。 |
| 29 | + |
| 30 | +**示例**: |
| 31 | + |
| 32 | +- 示例 1: |
| 33 | + |
| 34 | +```python |
| 35 | +输入:accounts = [["John", "johnsmith@mail.com", "john00@mail.com"], ["John", "johnnybravo@mail.com"], ["John", "johnsmith@mail.com", "john_newyork@mail.com"], ["Mary", "mary@mail.com"]] |
| 36 | +输出:[["John", 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com'], ["John", "johnnybravo@mail.com"], ["Mary", "mary@mail.com"]] |
| 37 | +解释: |
| 38 | +第一个和第三个 John 是同一个人,因为他们有共同的邮箱地址 "johnsmith@mail.com"。 |
| 39 | +第二个 John 和 Mary 是不同的人,因为他们的邮箱地址没有被其他帐户使用。 |
| 40 | +可以以任何顺序返回这些列表,例如答案 [['Mary','mary@mail.com'],['John','johnnybravo@mail.com'], |
| 41 | +['John','john00@mail.com','john_newyork@mail.com','johnsmith@mail.com']] 也是正确的。 |
| 42 | +``` |
| 43 | + |
| 44 | +- 示例 2: |
| 45 | + |
| 46 | +```python |
| 47 | +输入: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"]] |
| 48 | +输出:[["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"]] |
| 49 | +``` |
| 50 | + |
| 51 | +## 解题思路 |
| 52 | + |
| 53 | +### 思路 1:并查集 |
| 54 | + |
| 55 | +这道题的核心是将具有相同邮箱的账户合并在一起。可以使用并查集来解决。 |
| 56 | + |
| 57 | +**解题步骤**: |
| 58 | + |
| 59 | +1. 将每个邮箱看作一个节点,如果两个邮箱属于同一个账户,就将它们连接起来。 |
| 60 | +2. 使用哈希表 $email\_to\_id$ 记录每个邮箱对应的账户索引。 |
| 61 | +3. 使用并查集将属于同一个人的邮箱合并到同一个集合中。 |
| 62 | +4. 遍历所有账户,对于每个账户中的邮箱: |
| 63 | + - 如果邮箱第一次出现,记录其账户索引。 |
| 64 | + - 如果邮箱已经出现过,将当前账户与之前的账户合并。 |
| 65 | +5. 最后,将同一集合中的所有邮箱归类到一起,并按字典序排序。 |
| 66 | + |
| 67 | +### 思路 1:代码 |
| 68 | + |
| 69 | +```python |
| 70 | +class Solution: |
| 71 | + def accountsMerge(self, accounts: List[List[str]]) -> List[List[str]]: |
| 72 | + # 并查集 |
| 73 | + parent = {} |
| 74 | + |
| 75 | + def find(x): |
| 76 | + if x not in parent: |
| 77 | + parent[x] = x |
| 78 | + if parent[x] != x: |
| 79 | + parent[x] = find(parent[x]) |
| 80 | + return parent[x] |
| 81 | + |
| 82 | + def union(x, y): |
| 83 | + root_x = find(x) |
| 84 | + root_y = find(y) |
| 85 | + if root_x != root_y: |
| 86 | + parent[root_x] = root_y |
| 87 | + |
| 88 | + # 邮箱到账户索引的映射 |
| 89 | + email_to_id = {} |
| 90 | + # 邮箱到姓名的映射 |
| 91 | + email_to_name = {} |
| 92 | + |
| 93 | + # 遍历所有账户,建立邮箱之间的连接 |
| 94 | + for account in accounts: |
| 95 | + name = account[0] |
| 96 | + for i in range(1, len(account)): |
| 97 | + email = account[i] |
| 98 | + email_to_name[email] = name |
| 99 | + |
| 100 | + if email not in email_to_id: |
| 101 | + email_to_id[email] = email |
| 102 | + |
| 103 | + # 将当前账户的所有邮箱合并到第一个邮箱的集合中 |
| 104 | + union(account[1], email) |
| 105 | + |
| 106 | + # 将同一集合的邮箱归类 |
| 107 | + merged = {} |
| 108 | + for email in email_to_id: |
| 109 | + root = find(email) |
| 110 | + if root not in merged: |
| 111 | + merged[root] = [] |
| 112 | + merged[root].append(email) |
| 113 | + |
| 114 | + # 构建结果 |
| 115 | + result = [] |
| 116 | + for emails in merged.values(): |
| 117 | + name = email_to_name[emails[0]] |
| 118 | + result.append([name] + sorted(emails)) |
| 119 | + |
| 120 | + return result |
| 121 | +``` |
| 122 | + |
| 123 | +### 思路 1:复杂度分析 |
| 124 | + |
| 125 | +- **时间复杂度**:$O(n \log n)$,其中 $n$ 是所有邮箱的总数。并查集操作的时间复杂度接近 $O(1)$,主要时间消耗在排序上。 |
| 126 | +- **空间复杂度**:$O(n)$。需要存储并查集、哈希表等数据结构。 |
0 commit comments