721. 账户合并

132 阅读1分钟

题目描述

leetcode-cn.com/problems/ac…

分析

数组的每项一个字数组,然后第一项是 name,第二项开始是 emails,题目要求把具有相同邮箱的账号合并起来,因此我想到的就是并查集

算法,数据结构

哈希表,并查集

解题思路

对于一个并查集,最关键的就是亮点,fa 里存的是什么以及怎么把联通关系和题目对应起来

并查集的意义

这里的 fa 存储的是邮箱编号的联通关系

在这里我给每个邮箱一个单独的编号,然后把邮箱和它的编号放到一个 map 里做对应,把邮箱和它的名字做对应,放到另一个 map

再次遍历账户,对于一个账户下的邮箱,连接他们!,根据这个联通关系我们继续向下走

构建账户

有了这个联通关系,能干什么呢?

实际上,此时已经可以构建合并后的所有账户了

我把所有联通的邮箱放到一个数组里,然后构建出一个单独的账户

这里要进行遍历的,实际上是所有的 email, 找到他在联通集里的 index, 做一个单独的数组,这个数组里会放所有和他联通的邮箱

最后,再把名字放进数组(根据题目),排序所有邮箱

代码

var accountsMerge = function (accounts) {
  const emailToIndex = new Map()
  const emailToName = new Map()
  let emailsCount = 0

  for (const account of accounts) {
    const name = account[0] // email name
    const size = account.length // name + email address length
    for (let i = 1; i < size; i++) {
      // iterate each email addr
      const email = account[i] // email addr
      if (!emailToIndex.has(email)) {
        // map do not has this email recorded
        emailToIndex.set(email, emailsCount++) // unique email address index++
        emailToName.set(email, name) // assign an name to this email, each email has an unique name
      }
    }
  }

  const uf = new UnionFind(emailsCount)
  for (const account of accounts) {
    const firstEmail = account[1] // first email address in this account
    const firstIndex = emailToIndex.get(firstEmail) // the name assigned to this email above
    const size = account.length // items length of this account
    for (let i = 2; i < size; i++) {
      // iterate each email next to the first one in this account
      const nextEmail = account[i]
      const nextIndex = emailToIndex.get(nextEmail)
      uf.union(firstIndex, nextIndex) // merge them all in union set
    }
  }

  // how many accounts are there after merge
  const indexToEmails = new Map()
  for (const email of emailToIndex.keys()) {
    // iterate each email address
    const index = uf.find(emailToIndex.get(email)) // index of this email in DSU
    const account = indexToEmails.get(index) ? indexToEmails.get(index) : [] // if has already record the index, if so, then extract this account
    account.push(email)
    indexToEmails.set(index, account)
  }
  const merged = []
  for (const emails of indexToEmails.values()) {
    emails.sort() // according to ASCII
    const name = emailToName.get(emails[0])
    const account = [] // make 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]
  }
}