[路飞]_账户合并

216 阅读2分钟

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"。 
第二个 JohnMary 是不同的人,因为他们的邮箱地址没有被其他帐户使用。
可以以任何顺序返回这些列表,例如答案 
[
    ['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
}