leetcode 721. 账户合并【并查集】

题目

在这里插入图片描述

基础知识准备-并查集

  1. 查找并返回所在集合的根节点:find():
    注意:
    根节点特性是:father[i]==i; 每次查找时进行路径压缩,保证集合树的高度不大于2;
	int find(int i) { //寻找根节点
		int f = i;
		while (father[f] != f) //当该结点不是根节点时
			f = father[f]; //上诉到父结点
		//路径压缩
		while (i != f) {
			int tmp = father[i];
			father[i] = f;
			i = tmp;
		}
		return f; //根节点
	}
  1. 合并两个元素所在集合:combine():
	void combine(int i, int j) { //把j加入i集合
		int i_f = find(i); //查找所在集合的根节点
		int j_f = find(j);
		if (i_f != j_f) { //根节点不同时,不在一个集合,才合并
			father[j_f] = i_f;
		}
	}

题目思路

本题的重点是抽象、映射、确定哪里需要用到并查集。

  • 需要用到并查集的是用户编号,即accounts中的第一维编号(不能用用户名,可能出现重复),初始时,每个用户的父节点是自己:father[i]=i。
  • 遍历每个用户的每个邮箱:若邮箱mail第一次出现,记录所属的用户编号i,用unorder_map<string,int>mail_userid保存;若邮箱重复出现,合并当前用户i邮箱第一次出现的用户mail_userid[mail],这里才用到了并查集。
  • 然后,查看每个用户的父结点,若父结点不是自己,说明自己的邮箱和别的用户重复,把自己旗下的邮箱归入父结点门下。

C++代码(注释很详细~)

class Solution {
public:
	int father[1001]; //用户-》父用户的并查集映射
	int find(int i) { //寻找根节点
		int f = i;
		while (father[f] != f) //当该结点不是根节点时
			f = father[f]; //上诉到父结点
		//路径压缩
		while (i != f) {
			int tmp = father[i];
			father[i] = f;
			i = tmp;
		}
		return f; //根节点
	}
	void combine(int i, int j) { //把j加入i集合
		int i_f = find(i);
		int j_f = find(j);
		if (i_f != j_f) { //根节点不同时,不在一个集合,则合并
			father[j_f] = i_f;
		}
	}
	vector<vector<string>> accountsMerge(vector<vector<string>>& accounts) {
		int n = accounts.size();
		for (int i = 0; i < n; ++i)father[i] = i;//初始时,父亲是自己
		unordered_map<string, int>mail_user; //邮箱,所属用户编号
		for (int i = 0; i < n; ++i) {
			int len = accounts[i].size();
			for (int j = 1; j < len; ++j) {
				if (!mail_user.count(accounts[i][j])) //邮箱第一次出现
					mail_user[accounts[i][j]] = i;//<邮箱,用户编号>
				else combine(mail_user[accounts[i][j]], i); //把i用户的父用户设为第一次使用该邮箱的用户
			}
		}
		//把邮箱归于真正所属的父用户中(并set排序)
		unordered_map<int, set<string> >user_mails;//父用户编号,所含邮箱
		for (int i = 0; i < n; ++i) {
			int f_idx = find(i); //用户所属的 父用户编号
			for (int j = 1; j < accounts[i].size(); ++j)
				user_mails[f_idx].insert(accounts[i][j]); //把邮箱加入 所属父用户
		}
		//转化为规定形式 返回
		vector<vector<string>>ret;
		for (auto it : user_mails) {
			vector<string>tmp = { accounts[it.first][0] };//先加入用户名
			for (string mail : it.second)
				tmp.push_back(mail); //加入邮箱
			ret.push_back(tmp);
		}
		return ret;
	}
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值