[路飞]_账户合并

「这是我参与2022首次更文挑战的第37天,活动详情查看:2022首次更文挑战

leetcode-721 账户合并

题目介绍

原题目请见上方链接

解题思路

在此题中,根据题目意思归纳以下条件:

  • 每个数组的第一个元素是人的名称,从第二个元素开始是这个人的邮箱
  • 邮箱是唯一的,同一个邮箱必定属于同一个人
  • 人的名称不是唯一的,同一个名称不一定属于同一个人

要求:

  • 合并账户,有邮箱交集的两个账户必定是属于同一个人,需要将同一个人的所有账户进行合并
  • 合并账户之后,邮箱需要按照 字符 ASCII 顺序排列

我们要做的就是将同一个账户的所有邮箱进行合并去重,然后再跟对应的账户名称对应起来,就是一个账户

解题步骤

  1. 定义一个哈希表 emailIndexMap,用于缓存每个邮箱与编号之间的映射,并且对邮箱去重
  2. 定义一个哈希表 emailNameMap,用于缓存每个邮箱与账户名之间的映射,这是 1对1 的关系
  3. 遍历账户列表 accounts,将每个账户的账户名称和后面的邮箱列表分开
  4. 遍历邮箱列表,将邮箱与编号之间的映射关系缓存到 emailIndexMap 中,同时将邮箱与账户名称之间的映射关系缓存到 emailNameMap

至此,我们将题目中的数据形成了如下的两个映射关系表 1645631390(1).png

  1. 再次遍历账户列表 accounts,将属于同一个账户的邮箱编号进行连通,每个集合中的邮箱均属于同一个账户
  2. 定义一个哈希表 accountIndexMap,用于将邮箱按集合分离

1645632310(1).png

  1. 然后将每个集合中的邮箱按 字符 ASCII 顺序排序
  2. 最后根据集合中的邮箱在 emailNameMap 中找到对应的账户名称,往每个集合的头部插入账户名称
  3. 返回合并后的账户列表

解题代码

var accountsMerge = function(accounts) {
    const emailIndexMap = new Map()
    const emailNameMap = new Map()
    let index = 0
    for (const account of accounts) {
        const [name, ...accts] = account
        for (const acct of accts) {
            if (!emailIndexMap.has(acct)) {
                // 缓存邮箱与编号之间的映射关系
                emailIndexMap.set(acct, index++)
                // 缓存邮箱与账户名称之间的映射关系
                emailNameMap.set(acct, name)
            } 
        }
    }

    const unionSet = new UnionSet(emailIndexMap.size)
    for (const account of accounts) {
        const [name, ...accts] = account
        for (let i = 1; i < accts.length; i++) {
            // 将同一账户的邮箱编号进行连通
            unionSet.merge(emailIndexMap.get(accts[i - 1]), emailIndexMap.get(accts[i]))
        }
    }

    const accountIndexMap = new Map()
    // 将邮箱按集合进行分类
    for (const [email, index] of emailIndexMap) {
        const rootIndex = unionSet.get(index)
        if (!accountIndexMap.has(rootIndex)) accountIndexMap.set(rootIndex, [])
        accountIndexMap.get(rootIndex).push(email)
    }

    const res = []
    for (const arr of accountIndexMap.values()) {
        // 将邮箱按字符 ASCII 码排序
        arr.sort()
        // 往邮箱头部插入根据集合中的邮箱查找到的账户名称
        arr.unshift(emailNameMap.get(arr[0]))
        // 将当前账户插入到合并之后的账户列表中
        res.push(arr)
    }

    // 返回合并后的账户列表
    return res
};

// 并查集
class UnionSet {
    constructor(n) {
        this.fa = []
        this.size = []
        for (let i = 0; i < n; i++) {
            this.fa[i] = i
            this.size[i] = 1
        }
    }

    get(v) {
        if (this.fa[v] === v) return v
        const root = this.get(this.fa[v])
        this.fa[v] = root
        return root
    }

    merge(a, b) {
        const ra = this.get(a), rb = this.get(b)
        if (ra === rb) return
        if (this.size[ra] < this.size[rb]) {
            this.fa[ra] = rb
            this.size[rb] += this.size[ra]
        } else {
            this.fa[rb] = ra
            this.size[ra] += this.size[rb]
        }

    }
}
复制代码

Guess you like

Origin juejin.im/post/7067938468579508261