题目
给定一个列表 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']
]
也是正确的。
题解
并查集
- 同名账户是存在的,同名账户不一定是同一个账户
- 只有邮箱可以判断两个账户是不是同一个账户
- 并查集合并关系找到了,两个账户拥有同一个邮箱
- 因为邮箱分散在多个账户,不好处理,第一步是将邮箱展开下标放在map中;并且记录邮箱数量
- 根据邮箱数量设置并查集集合长度
- 再次枚举账户二维数组
- 将统一账户下的邮箱的下标合并
- 枚举邮箱下标map,根据map中记录的下标找到有共同祖先的邮箱下标;用祖先邮箱下标作为key,value是当前邮箱组成的数组,存放到object对象中
- 枚举object对象组装result(结果)数据
- 返回result
代码
class Union {
constructor(n) {
this.size = Array(n).fill(1)
this.list = Array(n)
.fill(0)
.map((v, i) => i)
}
find(x) {
if (x === this.list[x]) return x
const root = this.find(this.list[x])
this.list[x] = root
return root
}
merge(x, y) {
const rootX = this.find(x)
const rootY = this.find(y)
if (rootX === rootY) return
if (this.size[rootX] > this.size[rootY]) {
this.list[rootY] = rootX
this.size[rootX] += this.size[rootY]
} else {
this.list[rootX] = rootY
this.size[rootY] += this.size[rootX]
}
}
}
var accountsMerge = function (accounts) {
const emailMap = new Map()
const indexMap = new Map()
let emailNum = 0
const len = accounts.length
// 获取数据
for (let i = 0; i < len; i++) {
const account = accounts[i][0]
for (let j = 1; j < accounts[i].length; j++) {
const email = accounts[i][j]
indexMap.set(email, emailNum++)
emailMap.set(email, account)
}
}
const union = new Union(emailNum)
// 合并数据
for (let i = 0; i < len; i++) {
// 为什么取下标1的数据,因为下标为0的数据是账户,下标为1开始的数据才是邮箱
const fristEmail = accounts[i][1]
const firstInex = indexMap.get(fristEmail)
for (let j = 2; j < accounts[i].length; j++) {
const nextEmail = accounts[i][j]
const nextIndex = indexMap.get(nextEmail)
union.merge(firstInex, nextIndex)
}
}
const map = {}
indexMap.forEach((index, email) => {
const rootIndex = union.find(index)
if (map[rootIndex]) {
map[rootIndex].push(email)
} else {
map[rootIndex] = [email]
}
})
let result = []
Object.keys(map).forEach((k) => {
const array = map[k]
array.sort()
result.push([emailMap.get(array[0]), ...array])
})
return result
}