leetcode:721. 账户合并

该博客主要介绍了LeetCode中的721题,即如何合并同一用户的多个电子邮件账户。文章详细解析了使用并查集和深度优先搜索(DFS)算法来解决此问题的思路。首先,通过并查集建立各个账户的连接关系,然后利用DFS将所有同一个人的邮箱合并到一起,最终得到合并后的账户列表。
摘要由CSDN通过智能技术生成

题目来源

题目描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    vector<vector<string>> accountsMerge(vector<vector<string>>& accounts) {

    }
};

题目解析

题意:给出一堆账户和对应的邮箱。要求合并同一个人的多个邮箱账户。

并查集

由题意知道:

  • 存在相同邮箱的账号一定是同一个人
  • 名称相同的账户不一定是同一个人。

所以我们只能通过邮箱是否相同来判断是否是同一个人。

思路:

  • 先初始化每个账户为1个连通分量
  • 遍历每个账户下的邮箱,判断该邮箱是否在其他账户下出现
    • 如果未出现,继续
    • 如果账户A、B下出现了相同的邮箱email,那么将账户A和账户B两个连通分量进行合并
  • 最后遍历并查集中每个连通分量,将所有连通分量内部账户的邮箱全部合并(相同的去重,不同的合并)
class Solution {
    class UnionFind{
    private:
        std::vector<int> parent; // parent[i] = k : i的父亲是k
        std::vector<int> size;   // size[i] = k : 如果i是代表节点,size[i]才有意义( i所在的集合大小是多少),否则无意义
        std::vector<int> help;  //  辅助结构
        int cnt;               //一共有多少个集合
    public:
        explicit UnionFind(int n){
            cnt = n;
            parent.resize(n);
            size.resize(n);
            help.resize(n);
            for (int i = 0; i < n; ++i) {
                parent[i] = i;
                size[i] = 1;
            }
        }

        // 返回i的代表节点
        // 这个过程要做路径压缩
        int findRoot(int i){
            int hi = 0;
            while (i != parent[i]){
                help[hi++] = parent[i];
                i = parent[i];
            }
            for (hi--; hi >= 0; --hi) {
                parent[help[hi]] = i;
            }
            return i;
        }

        void merge(int i, int j){
            int f1 = findRoot(i);
            int f2 = findRoot(j);
            if(f1 != f2){
                if(size[f1] >= size[f2]){
                    parent[f2] = f1;
                    size[f1] = size[f1] + size[f2];
                }else{
                    parent[f1] = f2;
                    size[f2] = size[f2] + size[f1];
                }
                --cnt;
            }
        }

        int counts() const{
            return cnt;
        }
    };

public:
    vector<vector<string>> accountsMerge(vector<vector<string>>& accounts) {
        vector<vector<string>> res;

        // 作用:存储每个邮箱属于哪个账户 ,同时 在遍历邮箱时,判断邮箱是否出现过
        // 格式:<邮箱,账户id>
        std::unordered_map<std::string, int> mapEA;
        int n = accounts.size();
        UnionFind uf(n);
        for (int i = 0; i < n; ++i) {
            int m = accounts[i].size();
            for (int j = 1; j < m; ++j) {
                string s = accounts[i][j];
                if(mapEA.find(s) == mapEA.end()){
                    mapEA[s] = i;
                }else{
                    uf.merge(i, mapEA[s]);
                }
            }
        }

        // 作用: 存储每个账户下的邮箱
        // 格式: <账户id, 邮箱列表> >
        // 注意:这里的key必须是账户id,不能是账户名称,名称可能相同,会造成覆盖
        std::unordered_map<int, std::vector<std::string>> mapAEs;
        for(auto &it : mapEA){  // <邮箱,账户id>
            auto id = uf.findRoot(it.second);
            mapAEs[id].emplace_back(it.first);
        }
        for(auto &it : mapAEs){
            auto emails = it.second;
            auto id = it.first;
            std::sort(emails.begin(), emails.end());
            std::vector<std::string> tmp(1, accounts[id][0]);
            tmp.insert(tmp.end(), emails.begin(), emails.end());
            res.emplace_back(tmp);
        }

        return res;
    }
};

DFS

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值