721. 账户合并
给定一个列表 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)
是有效的邮箱地址原站题解
rust 解法, 执行用时: 28 ms, 内存消耗: 4.6 MB, 提交时间: 2024-07-15 07:46:34
impl Solution { pub fn accounts_merge(accounts: Vec<Vec<String>>) -> Vec<Vec<String>> { // dfs let mut graph = std::collections::HashMap::new(); let mut mail2account = std::collections::HashMap::new(); for i in 0..accounts.len() { let name = &accounts[i][0]; for j in 1..accounts[i].len() { mail2account.entry(&accounts[i][j]).or_insert(name); } for j in 2..accounts[i].len() { graph.entry(&accounts[i][j - 1]).or_insert(Vec::new()).push(&accounts[i][j]); graph.entry(&accounts[i][j]).or_insert(Vec::new()).push(&accounts[i][j - 1]); } } let mut visited = std::collections::HashSet::new(); let mut answer = Vec::new(); for i in 0..accounts.len() { if accounts[i].len() > 0 && !visited.contains(&accounts[i][1]) { let mut stack = vec![&accounts[i][1]]; let mut emails = Vec::new(); while !stack.is_empty() { let email = stack.pop().unwrap(); if visited.insert(email) { emails.push(email.to_string()); if let Some(nexts) = graph.get(email) { for next in nexts.iter() { stack.push(next); } } } } emails.sort_unstable(); let mut row = vec![mail2account.get(&accounts[i][1]).unwrap().to_string()]; row.append(&mut emails); answer.push(row); } } answer } }
rust 解法, 执行用时: 87 ms, 内存消耗: 3.9 MB, 提交时间: 2024-07-15 07:46:13
impl Solution { pub fn accounts_merge(accounts: Vec<Vec<String>>) -> Vec<Vec<String>> { // union-find let mut parents = std::collections::HashMap::new(); let mut mail2account = std::collections::HashMap::new(); for i in 0..accounts.len() { let name = &accounts[i][0]; for j in 1..accounts[i].len() { parents.entry(&accounts[i][j]).or_insert(&accounts[i][j]); mail2account.entry(&accounts[i][j]).or_insert(name); } } for i in 0..accounts.len() { for j in 2..accounts[i].len() { let mut a = &accounts[i][j - 1]; let mut b = &accounts[i][j]; while &a != parents.get(a).unwrap() { a = parents.get(a).unwrap(); } while &b != parents.get(b).unwrap() { b = parents.get(b).unwrap(); } if a == b { continue; } *parents.get_mut(b).unwrap() = a; } } let mut sets = std::collections::HashMap::new(); for (&k, &v) in parents.iter() { let mut root = k; while &root != parents.get(root).unwrap() { root = parents.get(root).unwrap(); } sets.entry(root).or_insert(Vec::new()).push(k); } let mut answer = Vec::new(); sets.into_iter().for_each(|(head, set)| { let mut row = vec![mail2account.get(head).unwrap().to_string()]; let mut mails: Vec<String> = set.into_iter().map(|s| s.to_string()).collect(); mails.sort_unstable(); row.append(&mut mails); answer.push(row); }); answer } }
golang 解法, 执行用时: 48 ms, 内存消耗: 7.8 MB, 提交时间: 2023-09-23 00:19:59
func accountsMerge(accounts [][]string) (ans [][]string) { emailToIndex := map[string]int{} emailToName := map[string]string{} for _, account := range accounts { name := account[0] for _, email := range account[1:] { if _, has := emailToIndex[email]; !has { emailToIndex[email] = len(emailToIndex) emailToName[email] = name } } } parent := make([]int, len(emailToIndex)) for i := range parent { parent[i] = i } var find func(int) int find = func(x int) int { if parent[x] != x { parent[x] = find(parent[x]) } return parent[x] } union := func(from, to int) { parent[find(from)] = find(to) } for _, account := range accounts { firstIndex := emailToIndex[account[1]] for _, email := range account[2:] { union(emailToIndex[email], firstIndex) } } indexToEmails := map[int][]string{} for email, index := range emailToIndex { index = find(index) indexToEmails[index] = append(indexToEmails[index], email) } for _, emails := range indexToEmails { sort.Strings(emails) account := append([]string{emailToName[emails[0]]}, emails...) ans = append(ans, account) } return }
javascript 解法, 执行用时: 124 ms, 内存消耗: 52.2 MB, 提交时间: 2023-09-23 00:19:40
/** * @param {string[][]} accounts * @return {string[][]} */ var accountsMerge = function(accounts) { const emailToIndex = new Map(); const emailToName = new Map(); let emailsCount = 0; for (const account of accounts) { const name = account[0]; const size = account.length; for (let i = 1; i < size; i++) { const email = account[i]; if (!emailToIndex.has(email)) { emailToIndex.set(email, emailsCount++); emailToName.set(email, name); } } } const uf = new UnionFind(emailsCount); for (const account of accounts) { const firstEmail = account[1]; const firstIndex = emailToIndex.get(firstEmail); const size = account.length; for (let i = 2; i < size; i++) { const nextEmail = account[i]; const nextIndex = emailToIndex.get(nextEmail); uf.union(firstIndex, nextIndex); } } const indexToEmails = new Map(); for (const email of emailToIndex.keys()) { const index = uf.find(emailToIndex.get(email)); const account = indexToEmails.get(index) ? indexToEmails.get(index) : []; account.push(email); indexToEmails.set(index, account); } const merged = []; for (const emails of indexToEmails.values()) { emails.sort(); const name = emailToName.get(emails[0]); const account = []; account.push(name); account.push(...emails); merged.push(account); } return merged; }; class UnionFind { constructor (n) { this.parent = new Array(n).fill(0).map((element, index) => index); } union (index1, index2) { this.parent[this.find(index2)] = this.find(index1); } find (index) { if (this.parent[index] !== index) { this.parent[index] = this.find(this.parent[index]); } return this.parent[index]; } }
java 解法, 执行用时: 31 ms, 内存消耗: 46.4 MB, 提交时间: 2023-09-23 00:19:23
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 { 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]; } }
cpp 解法, 执行用时: 160 ms, 内存消耗: 54.9 MB, 提交时间: 2023-09-23 00:18:44
class UnionFind{ private: unordered_map<string,string> father; // 根节点所在集合的所有账户 unordered_map<string,vector<string>> accounts; public: unordered_map<string,string> get_father(){ return father; } unordered_map<string,vector<string>> get_accounts(){ return accounts; } string find(string s){ if(father[s] == "root") return s; // 递归的路径压缩处理 father[s] = find(father[s]); return father[s]; } void merge(string x,string y){ string root_x = find(x); string root_y = find(y); if(root_x == root_y) return; // 按秩合并,更新根节点和所属的账户 if(accounts[root_x].size() < accounts[root_y].size()){ father[root_x] = root_y; for(auto& account: accounts[root_x]) accounts[root_y].push_back(account); accounts.erase(root_x); }else{ father[root_y] = root_x; for(auto& account: accounts[root_y]) accounts[root_x].push_back(account); accounts.erase(root_y); } } void add(string x){ if(!father.count(x)){ father[x] = "root"; accounts[x] = {x}; } } }; class Solution { public: vector<vector<string>> accountsMerge(vector<vector<string>>& accounts) { UnionFind uf; // email -> name unordered_map<string,string> email_to_name; for(auto& v: accounts){ // 找到姓名和主账户 string name = v[0]; string master = v[1]; email_to_name[master] = name; uf.add(master); // 和其余的账户一一合并 for(int i = 2;i < v.size();i++){ email_to_name[v[i]] = name; uf.add(v[i]); uf.merge(master,v[i]); } } vector<vector<string>> res; unordered_map<string,string> father = uf.get_father(); unordered_map<string,vector<string>> acc = uf.get_accounts(); for(auto& p: father){ // 是根节点 if(p.second == "root"){ // 添加user vector<string> user_account = {email_to_name[p.first]}; // 添加账户 sort(acc[p.first].begin(),acc[p.first].end()); for(auto& email: acc[p.first]) user_account.push_back(email); res.push_back(user_account); } } return res; } };
cpp 解法, 执行用时: 120 ms, 内存消耗: 50.5 MB, 提交时间: 2023-09-23 00:17:44
class Solution { public: unordered_map<string,vector<string>> build_graph(vector<vector<string>>& accounts){ /* 建图 */ unordered_map<string,vector<string>> graph; for(auto& account: accounts){ string master = account[1]; // 对剩余账户做一个去重 for(auto& email: unordered_set<string>(account.begin()+2,account.end())){ graph[master].push_back(email); graph[email].push_back(master); } } return graph; } void dfs(unordered_map<string,vector<string>>& graph,unordered_set<string>& visited,string& email,vector<string>& emails){ /* 深搜遍历 */ // 已经访问过的就剪枝 if(visited.count(email)) return; visited.insert(email); emails.push_back(email); // 对邻居节点继续深搜 for(auto& neighbor: graph[email]) dfs(graph,visited,neighbor,emails); } vector<vector<string>> accountsMerge(vector<vector<string>>& accounts) { // 建图 unordered_map<string,vector<string>> graph = build_graph(accounts); vector<vector<string>> res; unordered_set<string> visited; for(auto& account: accounts){ vector<string> emails; // 深搜得到name的所有邮箱 dfs(graph,visited,account[1],emails); if(emails.empty()) continue; // 排序,添加姓名,加入到答案中 sort(emails.begin(),emails.end()); emails.insert(emails.begin(),account[0]); res.push_back(emails); } return res; } };
python3 解法, 执行用时: 84 ms, 内存消耗: 20.6 MB, 提交时间: 2023-09-23 00:17:29
class Solution: def build_graph(self,accounts): """ 建图 """ graph = collections.defaultdict(list) for account in accounts: master = account[1] # 对剩余账户做一个去重 for email in list(set(account[2:])): graph[master].append(email) graph[email].append(master) return graph def dfs(self,email,graph,visited,emails): """ 深搜遍历 """ # 已经访问过的就剪枝 if email in visited: return visited.add(email) emails.append(email) # 对邻居节点继续深搜 for neighbor in graph[email]: self.dfs(neighbor,graph,visited,emails) def accountsMerge(self, accounts: List[List[str]]) -> List[List[str]]: graph = self.build_graph(accounts) res = [] visited = set() for account in accounts: emails = [] self.dfs(account[1],graph,visited,emails) if emails: res.append([account[0]] + sorted(emails)) return res
python3 解法, 执行用时: 112 ms, 内存消耗: 19.5 MB, 提交时间: 2023-09-23 00:17:19
class UnionFind: def __init__(self): self.father = {} # 根节点所在集合的所有账户 self.accounts = {} def find(self,x): if not self.father[x]: return x # 递归的路径压缩处理 self.father[x] = self.find(self.father[x]) return self.father[x] def merge(self,x,y): root_x,root_y = self.find(x),self.find(y) if root_x == root_y: return # 按秩合并,更新根节点和所属的账户 if len(self.accounts[root_x]) > len(self.accounts[root_y]): self.father[root_y] = root_x self.accounts[root_x] += self.accounts[root_y] del self.accounts[root_y] else: self.father[root_x] = root_y self.accounts[root_y] += self.accounts[root_x] del self.accounts[root_x] def add(self,x): if x not in self.father: self.father[x] = None self.accounts[x] = [x] class Solution: def accountsMerge(self, accounts: List[List[str]]) -> List[List[str]]: uf = UnionFind() for account in accounts: # 找到姓名和主账户 name,master = account[0],account[1] uf.add((name,master)) # 和其余的账户一一合并 account = list(set(account[2:])) for i in range(len(account)): uf.add((name,account[i])) uf.merge((name,master),(name,account[i])) res = [] for key,value in uf.father.items(): # 是根节点 if not value: # 添加user usr = [uf.accounts[key][0][0]] acc = [] # 添加账户 for account in uf.accounts[key]: acc.append(account[1]) res.append(usr + sorted(acc)) return res