[Swift]LeetCode721. 帳戶合併 | Accounts Merge

★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤微信公衆號:山青詠芝(shanqingyongzhi)
➤博客園地址:山青詠芝(https://www.cnblogs.com/strengthen/
➤GitHub地址:https://github.com/strengthen/LeetCode
➤原文地址: http://www.javashuo.com/article/p-cejjzhfc-me.html 
➤若是連接不是山青詠芝的博客園地址,則多是爬取做者的文章。
➤原文已修改更新!強烈建議點擊原文地址閱讀!支持做者!支持原創!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★html

Given a list accounts, each element accounts[i] is a list of strings, where the first element accounts[i][0] is a name, and the rest of the elements are emails representing emails of the account.git

Now, we would like to merge these accounts. Two accounts definitely belong to the same person if there is some email that is common to both accounts. Note that even if two accounts have the same name, they may belong to different people as people could have the same name. A person can have any number of accounts initially, but all of their accounts definitely have the same name.github

After merging the accounts, return the accounts in the following format: the first element of each account is the name, and the rest of the elements are emails in sorted order. The accounts themselves can be returned in any order.微信

Example 1:app

Input: 
accounts = [["John", "johnsmith@mail.com", "john00@mail.com"], ["John", "johnnybravo@mail.com"], ["John", "johnsmith@mail.com", "john_newyork@mail.com"], ["Mary", "mary@mail.com"]]
Output: [["John", 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com'],  ["John", "johnnybravo@mail.com"], ["Mary", "mary@mail.com"]]
Explanation: 
The first and third John's are the same person as they have the common email "johnsmith@mail.com".
The second John and Mary are different people as none of their email addresses are used by other accounts.
We could return these lists in any order, for example the answer [['Mary', 'mary@mail.com'], ['John', 'johnnybravo@mail.com'], 
['John', 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com']] would still be accepted.

Note:spa

  • The length of accounts will be in the range [1, 1000].
  • The length of accounts[i] will be in the range [1, 10].
  • The length of accounts[i][j] will be in the range [1, 30].

給定一個列表 accounts,每一個元素 accounts[i] 是一個字符串列表,其中第一個元素 accounts[i][0] 是 名稱 (name),其他元素是 emails 表示該賬戶的郵箱地址。rest

如今,咱們想合併這些賬戶。若是兩個賬戶都有一些共同的郵件地址,則兩個賬戶一定屬於同一我的。請注意,即便兩個賬戶具備相同的名稱,它們也可能屬於不一樣的人,由於人們可能具備相同的名稱。一我的最初能夠擁有任意數量的賬戶,但其全部賬戶都具備相同的名稱。code

合併賬戶後,按如下格式返回賬戶:每一個賬戶的第一個元素是名稱,其他元素是按順序排列的郵箱地址。accounts 自己能夠以任意順序返回。orm

例子 1:htm

Input: 
accounts = [["John", "johnsmith@mail.com", "john00@mail.com"], ["John", "johnnybravo@mail.com"], ["John", "johnsmith@mail.com", "john_newyork@mail.com"], ["Mary", "mary@mail.com"]]
Output: [["John", 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com'],  ["John", "johnnybravo@mail.com"], ["Mary", "mary@mail.com"]]
Explanation: 
  第一個和第三個 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']]仍然會被接受。

注意:

  • accounts的長度將在[1,1000]的範圍內。
  • accounts[i]的長度將在[1,10]的範圍內。
  • accounts[i][j]的長度將在[1,30]的範圍內。

552ms

  1 class Solution {
  2     func accountsMerge(_ accounts: [[String]]) -> [[String]] {
  3         var dsu = DSU()
  4         for account in accounts {
  5             var emails = Array(account[1...])
  6             var name = account[0]
  7             dsu.addEdge(emails, name)
  8         }
  9         return dsu.unions()
 10     }
 11 }
 12 
 13 struct DSU {
 14     var mapping: [String: Int] = [:]
 15     var arr: [Int]
 16     var size: [Int]
 17     var comps: Int
 18     var names: [Int: String] = [:]
 19     
 20     mutating func unions() -> [[String]] {
 21         var res = [String: [String]]()
 22         for (email, index) in mapping {
 23             let parent = find(index)
 24             res["\(names[parent]!):\(parent)", default: []].append(email)
 25         }
 26         return res.map { arg0 in
 27             var (key, val) = arg0
 28             key = String(key.split(separator: ":")[0])
 29             return [key] + val.sorted()
 30         }
 31     }
 32     
 33     init() {
 34         arr = []
 35         size = []
 36         comps = 0
 37     }
 38     
 39     init(n: Int) {
 40         comps = n
 41         arr = Array(repeating: 0, count: n)
 42         size = Array(repeating: 1, count: n)
 43         for i in 0..<n {
 44             arr[i] = i
 45         }
 46     }
 47     
 48     mutating func addEdge(_ edge: [String], _ name: String) {
 49         if mapping[edge[0]] == nil {
 50             arr.append(arr.count)
 51             size.append(1)
 52             mapping[edge[0]] = arr.count - 1
 53             names[arr.count - 1] = name
 54         }
 55         for i in 1..<edge.count {
 56             if mapping[edge[i]] == nil {
 57                 arr.append(arr.count)
 58                 size.append(1)
 59                 mapping[edge[i]] = arr.count - 1
 60             }
 61             union(mapping[edge[0]]!, mapping[edge[i]]!)
 62         }
 63     }
 64     
 65     mutating func find(_ a: String, _ b: String) -> Bool {
 66         guard let indA = mapping[a], let indB = mapping[b] else { return false }
 67         return find(indA, indB)
 68     }
 69     
 70     mutating func find(_ a: Int) -> Int {
 71         return root(a)
 72     }
 73     
 74     mutating func union(_ a: Int, _ b: Int) {
 75         let rootA = root(a)
 76         let rootB = root(b)
 77         if rootA == rootB {
 78             return
 79         }
 80         if size[a] >= size[b] {
 81             arr[rootB] = rootA
 82             size[a] += size[b]
 83         } else {
 84             arr[rootA] = rootB
 85             size[b] += size[a]
 86         }
 87         comps -= 1
 88     }
 89     
 90     mutating func find(_ a: Int, _ b: Int) -> Bool {
 91         return root(a) == root(b)
 92     }
 93     
 94     private mutating func root(_ index: Int) -> Int {
 95         var index = index
 96         while arr[index] != index {
 97             arr[index] = arr[arr[index]]
 98             index = arr[index]
 99         }
100         return index
101     }
102 }

704ms

 1 class Solution {
 2     class UnionManager {
 3         var idMap = [String: Int]()
 4         var setMap = [Int]()
 5         
 6         func add(element: String) -> Int {
 7             if idMap[element] == nil{
 8                 let idForElement = setMap.count
 9                 idMap[element] = idForElement
10                 setMap.append(idForElement)
11             }
12             return idMap[element] ?? 0
13         }
14         
15         func findSet(id: Int) -> Int {
16             if setMap[id] != id {
17                 setMap[id] = findSet(id: setMap[id])
18             }
19             return setMap[id]
20         }
21         
22         func union(setOf string1: String, with string2: String) {
23             let id1 = add(element: string1)
24             let id2 = add(element: string2)
25             setMap[findSet(id: id1)] = findSet(id: id2)
26         }
27     }
28     
29     func accountsMerge(_ accounts: [[String]]) -> [[String]] {
30         let unionManager = UnionManager()
31         var emailToName = [String: String]()
32         let nonEmptyAccounts = accounts.filter{ $0.count > 1 }
33         for account in nonEmptyAccounts {
34             let name = account[0]
35             for email in account[1..<account.count] {
36                 emailToName[email] = name
37                 unionManager.union(setOf: email, with: account[1])
38             }
39         }
40         var results: [Int: [String]] = [:]
41         for (email, id) in unionManager.idMap {
42             let setId = unionManager.findSet(id: id)
43             results[setId, default: []].append(email)
44         }
45         return results.values.map { $0.sorted() }
46         .map { emailList in
47               if let name = emailToName[emailList[0]] {
48                   return [name] + emailList
49               } else {
50                   return emailList
51               }
52          }
53     }
54 }

732ms

  1 class Solution {
  2     func accountsMerge(_ accounts: [[String]]) -> [[String]] {
  3         guard accounts.count > 0 else {
  4             return []
  5         }
  6         let graph = Graph<String>(.undirected)
  7         var emailToNameDictionary: [String: String] = [:]
  8         for account in accounts {
  9             let name = account[0]
 10             let source = graph.createVertex(account[1])
 11             for i in 1..<account.count {
 12                 let email = account[i]
 13                 emailToNameDictionary[email] = name
 14                 if i > 1 {
 15                     let destination = graph.createVertex(account[i])
 16                     graph.addUndirectedEdge(between: source, and: destination)
 17                 }
 18             }
 19         }
 20         var mergedAccounts: [[String]] = []
 21         var discovered: Set<Vertex<String>> = []
 22         for vertex in graph.vertice {
 23             if !discovered.contains(vertex) {
 24                 var emails: [String] = []
 25                 depthFirstSearch(graph, vertex, &discovered, &emails)
 26                 let result = [emailToNameDictionary[vertex.val]!] + emails.sorted()
 27                 mergedAccounts.append(result)
 28             }
 29         }
 30         
 31         return mergedAccounts
 32     }
 33 }
 34 
 35 func depthFirstSearch<T: Hashable>(_ graph: Graph<T>, 
 36                                    _ source: Vertex<T>, 
 37                                    _ discovered: inout Set<Vertex<T>>, 
 38                                    _ emails: inout [T]
 39                                   ) {
 40     discovered.insert(source)
 41     emails.append(source.val)
 42     for edge in graph.edges(of: source) {
 43         let destination = edge.destination
 44         if !discovered.contains(destination) {
 45             depthFirstSearch(graph, destination, &discovered, &emails)
 46         }
 47     }
 48 }
 49 
 50 
 51 enum GraphType {
 52     case directed 
 53     case undirected 
 54 }
 55 
 56 struct Vertex<T: Hashable>: Hashable {
 57     public var val: T 
 58     public init(_ val: T) {
 59         self.val = val
 60     }
 61     
 62     public var hashValue : Int {
 63         return val.hashValue
 64     }
 65     
 66     public static func ==(lhs: Vertex<T>, rhs: Vertex<T>) -> Bool {
 67         return lhs.val == rhs.val
 68     }
 69 }
 70 
 71 struct Edge<T: Hashable> {
 72     public let source: Vertex<T>
 73     public let destination: Vertex<T>
 74     public let weight: Double? 
 75     public init(source: Vertex<T>, destination: Vertex<T>, weight: Double? = nil ) {
 76         self.source = source 
 77         self.destination = destination 
 78         self.weight = weight 
 79     }
 80 }
 81 
 82 
 83 class Graph<T: Hashable> {
 84     public var vertice: [Vertex<T>] = []
 85     private var adjacencyList: [Vertex<T>: [Edge<T>]] = [:]
 86     public var type: GraphType 
 87     
 88     public init(_ type: GraphType) {
 89         self.type = type
 90     }
 91     
 92     public func createVertex(_ val: T) -> Vertex<T> {
 93         let source = Vertex(val)
 94         if adjacencyList[source] == nil {
 95             adjacencyList[source] = []
 96             vertice.append(source)
 97         }
 98         return source 
 99     }
100     
101     public func addDirectedEdge(from source: Vertex<T>, 
102                                 to destination: Vertex<T>, 
103                                 weight: Double? = nil 
104                                ) {
105         let edge = Edge(source: source, destination: destination, weight: weight)
106         adjacencyList[source]?.append(edge)
107     }
108     
109     public func addUndirectedEdge(between source: Vertex<T>,
110                                   and destination: Vertex<T>, 
111                                   weight: Double? = nil 
112                                  ) {
113         addDirectedEdge(from: source, to: destination, weight: weight)
114         addDirectedEdge(from: destination, to: source, weight: weight)
115     }
116     
117     public func edges(of source: Vertex<T>) -> [Edge<T>] {
118         return adjacencyList[source] ?? []
119     }
120     
121     public func weight(from source: Vertex<T>, 
122                        to destination: Vertex<T>
123                       ) -> Double? {
124         return adjacencyList[source]?.first{ $0.destination == destination }?.weight 
125     }
126 }

852ms

 1 class Solution {
 2 // father[兒子] = 老大哥
 3 var father = [Int: Int]()
 4 
 5 func accountsMerge(_ accounts: [[String]]) -> [[String]] {
 6     
 7     // 1 union
 8     let emailToID = getEmailToID(accounts)
 9     for email in emailToID.keys {
10         let ids = emailToID[email]!
11         for i in ids {
12             union(i, ids[0])
13         }
14     }
15     
16     //2 merge
17     let idToEmailSet = getIdToEmailSet(accounts)
18     var mergedAccounts = [[String]]()
19     
20     for id in idToEmailSet.keys {
21         var sortedEmails = idToEmailSet[id]!.sorted()
22         sortedEmails.insert(accounts[id][0], at: 0)
23         mergedAccounts.append(sortedEmails)
24     }
25     
26     return mergedAccounts
27 }
28 
29 //find -> 找老大哥
30 func find(_ id: Int) -> Int {
31     var id = id
32     
33     var path = [Int]()
34     while let nextID = father[id] {
35         path.append(id)
36         id = nextID
37         
38     }
39     
40     for i in path {
41         father[i] = id
42     }
43     
44     return id
45 }
46 
47 // union
48 func union(_ a: Int, _ b: Int) {
49     let rootA = find(a)
50     let rootB = find(b)
51     if rootA != rootB {
52         father[rootA] = rootB
53     }
54 }
55 
56 
57 // 這個只是data處理
58 func getEmailToID(_ accounts: [[String]]) -> [String: [Int]] {
59     var emailToID = [String: [Int]]()
60     
61     for (userID, acnt) in accounts.enumerated() {
62         for i in 1..<acnt.count {
63             let curEmail = acnt[i]
64             var ids = emailToID[curEmail, default: [Int]()]
65             ids.append(userID)
66             emailToID[curEmail] = ids
67         }
68     }
69     
70     return emailToID
71 }
72 
73 func getIdToEmailSet(_ accounts: [[String]]) -> [Int: Set<String>] {
74     var idToEmailSet = [Int: Set<String>]()
75     for id in 0..<accounts.count {
76         let root_id = find(id)
77         var emailSet = idToEmailSet[root_id, default: Set<String>()]
78         
79         let account = accounts[id]
80         for i in 1..<account.count {
81             emailSet.insert(account[i])
82         }
83         idToEmailSet[root_id] = emailSet
84     }
85     
86     return idToEmailSet
87   }
88 }

864ms

 1 class Solution {
 2     func accountsMerge(_ accounts: [[String]]) -> [[String]] {
 3         guard accounts.count > 0 else {
 4             return []
 5         }
 6         
 7         var emailToName: [String: String] = [:]
 8         var unionFind = UnionFind<String>()
 9         for account in accounts {
10             let name = account[0]
11             let firstEmail = account[1]
12             unionFind.addSet(firstEmail)
13             for i in 1..<account.count {
14                 let email = account[i]
15                 emailToName[email] = name
16                 
17                 if i > 1 {
18                     let email = account[i]
19                     unionFind.addSet(email)
20                     unionFind.union(firstEmail, email)
21                 }
22             }
23         }
24         
25         var mergedAccount: [[String]] = []
26         var storage: [Int: [String]] = [:]
27         for element in unionFind.index.keys {
28             let parentIndex = unionFind.find(element)!
29             if storage[parentIndex] == nil {
30                 storage[parentIndex] = [element]
31             } else {
32                 storage[parentIndex]!.append(element)
33             }
34         }
35         
36         for emails in storage.values {
37             let name = emailToName[emails.first!]!
38             let result = [name] + emails.sorted()
39             mergedAccount.append(result)
40         }
41         
42         
43         return mergedAccount
44     }
45 }
46 
47 
48 struct UnionFind<T: Hashable> {
49     public var index: [T: Int] = [:]
50     private var parent: [Int] = []
51     private var size: [Int] = []
52     
53     public mutating func addSet(_ element: T) {
54         if index[element] == nil {
55             index[element] = parent.count 
56             parent.append(parent.count)
57             size.append(1)
58         }
59     }
60     
61     public mutating func find(_ element: T) -> Int? {
62         guard let index = index[element] else {
63             return nil 
64         }
65         
66         return setByIndex(index)
67     }
68     
69     private mutating func setByIndex(_ index: Int) -> Int {
70         if parent[index] == index {
71             return index 
72         } else {
73             parent[index] = setByIndex(parent[index])
74             return parent[index]
75         }
76     }
77     
78     public mutating func union(_ element1: T, _ element2: T) {
79         guard let set1 = find(element1), let set2 = find(element2) else {
80             return 
81         }
82         
83         if set1 != set2 {
84             if set1 > set2 {
85                 parent[set2] = set1
86                 size[set1] += size[set2]
87             } else {
88                 parent[set1] = set2
89                 size[set2] += size[set1]
90             }
91         }
92     }
93 }

Runtime: 1180 ms
Memory Usage: 21 MB
 1 class Solution {
 2     func accountsMerge(_ accounts: [[String]]) -> [[String]] {
 3         var res:[[String]] = [[String]]()
 4         var root:[String:String] = [String:String]()
 5         var owner:[String:String] = [String:String]()
 6         var m:[String:Set<String>] = [String:Set<String>]()
 7         for account in accounts
 8         {
 9             for i in 1..<account.count
10             {
11                 root[account[i]] = account[i]
12                 owner[account[i]] = account[0]
13             }
14         }
15         for account in accounts
16         {
17             var p:String = find(account[1], &root)
18             for i in 2..<account.count
19             {
20                 root[find(account[i], &root)] = p                
21             }
22         }
23         for account in accounts
24         {
25             for i in 1..<account.count
26             {
27                 let str:String = find(account[i], &root)
28                 m[str,default:Set<String>()].insert(account[i])
29             }
30         }
31         for (key,val) in m
32         {
33             var v:[String] = val.sorted()
34             v.insert(owner[key,default:String()],at:0)
35             res.append(v)
36         }
37         return res
38     }
39     
40     func find(_ s:String,_ root:inout [String:String]) -> String
41     {
42         return root[s] == s ? s : find(root[s,default:String()], &root)
43     }
44 }

2348ms

 1 class Solution {    
 2     func accountsMerge(_ accounts: [[String]]) -> [[String]] {
 3         var emailIdDict: [String:Int] = [:]
 4         var idNameDict: [Int:String] = [:]
 5         var idEmailsDict: [Int:[String]] = [:]
 6         
 7         var curId = 0
 8         
 9         for account in accounts {
10             // Get name
11             let name = account[0]
12             
13             // Make sure emails exist
14             guard account.count > 1 else {
15                 idNameDict[curId] = name
16                 curId += 1
17                 continue
18             }
19             
20             // Get emails
21             let emails = Array(account[1..<account.count])
22             
23             // Get matching ids for emails
24             var idMatches: [Int] = []
25             emails.forEach({
26                 guard let id = emailIdDict[$0] else { return }
27                 idMatches.append(id)
28             })
29             idMatches = Array(Set(idMatches)).sorted()
30             
31             // Get all combined emails
32             var allEmails: [String] = emails
33             idMatches.forEach({
34                 allEmails += idEmailsDict[$0] ?? []
35                 idNameDict.removeValue(forKey: $0)
36                 idEmailsDict.removeValue(forKey: $0)
37             })
38             
39             // Write data to dicts
40             allEmails.forEach({ emailIdDict[$0] = curId })
41             idNameDict[curId] = name
42             idEmailsDict[curId] = allEmails
43             
44             // Incrememt ID
45             curId += 1
46         }
47         
48         return Array(idNameDict.keys).compactMap({
49             guard let name = idNameDict[$0] else { return nil }
50             let emails = Array(Set((idEmailsDict[$0] ?? []))).sorted()
51             return [name] + emails
52         })
53     }
54 }

2708ms

 1 class Solution {
 2     func accountsMerge(_ accounts: [[String]]) -> [[String]] {
 3         var mAccountsMap = [String: [Set<String>]]()
 4         for sa in accounts {
 5             let sets = mAccountsMap[sa[0]] ?? [Set<String>]()
 6             let mergeset = Set(Array(sa[1..<sa.count]))
 7             let (mergesets, _) = mergeSets(mergeset, sets)
 8             // print(mergesets)
 9             mAccountsMap[sa[0]] = mergesets
10         }
11 
12         var result = [[String]]()
13         for (act, mailsSetArr) in mAccountsMap {
14             for set in mailsSetArr {            
15                 var line = [String]()
16                 line.append(act)
17                 line.append(contentsOf: Array(set.sorted()))
18                 result.append(line)
19             }
20         }
21         return result
22     }
23 
24     func mergeSets(_ mergeSet: Set<String>, _ sets: [Set<String>]) -> ([Set<String>], Bool) {
25         var ans = sets
26         var merged = false
27         var valildUionSet = Set<String>()
28         for i in 0..<sets.count {
29             let set = sets[i]
30             if !set.intersection(mergeSet).isEmpty {
31                 var uniSets = set
32                 uniSets.formUnion(mergeSet)
33                 merged = true
34                 ans.remove(at: i)
35                 valildUionSet = uniSets
36                 break
37             }
38         }
39         if !merged {
40             ans.append(mergeSet)
41         }
42         return merged ? mergeSets(valildUionSet, ans) : (ans, false)
43     }
44 }

2852ms

 1 class Solution {
 2     func accountsMerge(_ accounts: [[String]]) -> [[String]] {
 3         var name = ""
 4         var emailSets = [Set<String>]()
 5         var results = [[String]]()
 6         var sortedAccounts = accounts.sorted { return $0[0] < $1[0] }
 7         for account in sortedAccounts {
 8             if !name.isEmpty && account[0] != name {
 9                 for setFound in emailSets {
10                     results.append([name] + Array(setFound).sorted())
11                 }
12                 emailSets = []
13             }
14             name = account[0]
15             var currentEmailSet = Set(account[1..<account.count])
16             var newSets = [Set<String>]()
17             for (i, existingSet) in emailSets.enumerated() {
18                 if !currentEmailSet.intersection(existingSet).isEmpty {
19                     currentEmailSet = currentEmailSet.union(existingSet)
20                 } else {
21                     newSets.append(existingSet)
22                 }
23             }
24             newSets.append(currentEmailSet)
25             emailSets = newSets
26         }
27         for setFound in emailSets {
28             results.append([name] + Array(setFound).sorted())
29         }
30         return results
31     }
32 }
相關文章
相關標籤/搜索