C++--数据结构--并查集--高阶0711

1. 并查集

在一些应用问题中,需要将n个不同的元素划分成一些不相交的集合。开始时,每个元素自成一个 单元素集合,然后按一定的规律将归于同一组元素的集合合并。在此过程中要反复用到查询某一 个元素归属于那个集合的运算。适合于描述这类问题的抽象数据类型称为并查集(union-find set)。

某公司有10人,A地招4人,B地招3人,C地招3人,10个人起先互不相识,每个都是一个独立的小团体。现给这些人进行编号:{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};进行映射。

给以下数组用来存储该小集体,数组中的数字代表:该小集体中具有成员的个数。(-1代表的意义就是有一个人,负号的作用下文会体现)

后来自统一地区的人互相熟悉,建立小团体。

 我们如何进行集合合并操作呢?

1.2原理

  1. 数组的下标对应集合中元素的编号

  2. 数组中如果为负数,负号代表根,数字代表该集合中元素个数

  3. 数组中如果为非负数,代表该元素父亲在数组中的下标

以编号0和6为例,我们选0作为父节点,将6编号的集合中的数加给0编号的在集合中的数。再让编号6在集合中的值存放父节点的下标。

 综上 我们把三个集合表示完毕

 1.2.2 合并的代码

	size_t FindRoot(int x)
	{
        int root=x;
       	while (_ufs[root] >= 0)
		{
			root = _ufs[root];
		}

		// 路径压缩
		while (_ufs[x] >= 0)
		{
			int parent = _ufs[x];
			_ufs[x] = root;
			x = parent;
		}

		return root;

	}

	void Union(int x1, int x2)
	{
		int root1 = FindRoot(x1);
		int root2 = FindRoot(x2);

		// 控制数据量小的往大的集合合并
		if (abs(_ufs[root1]) < abs(_ufs[root2]))
			swap(root1, root2);

		if(root1 != root2)
		{
			_set[root1] += _set[root2];
			_set[root2] = root1;
		}
	}

1.3相关练习

力扣

省份的数量

class Solution {
public:
    int findroot(vector<int>&ufs,int n)
    {
        while(ufs[n]>=0)
        {
            n=ufs[n];
        }
        return n;
    }
    int findCircleNum(vector<vector<int>>& isConnected)
    {
        vector<int>ufs(isConnected.size(),-1);
        for(int i=0;i<isConnected.size();i++)
        {
            for(int j=0;j<isConnected[i].size();j++)
            {
                if(isConnected[i][j]==1)
                {
                    int root1=findroot(ufs,i);
                    int root2=findroot(ufs,j);
                    if(root1!=root2)
                    {
                        ufs[root1]+=ufs[root2];
                        ufs[root2]=root1;
                    }
                }
            }
        }
        int n=0;
        for(auto& e:ufs)
        {
            if(e<0) n++;
        }
        return n;
    }
};

力扣

等式方程的可满足性

class Solution {
public:
    int findroot(int n,vector<int>& ufs)
    {
        while(ufs[n]>=0)
        {
            n=ufs[n];
        }
        return n;
    }
    bool equationsPossible(vector<string>& equations) 
    {
        vector<int>ufs(26,-1);//每个字母都建立映射关系 a映射0 b映射1

        //第一遍 遍历将相同的元素放到同一个集合
        for(auto& str:equations)
        {
            if(str[1]=='=') //两个字母是相等关系
            {
                int r1=findroot(str[0]-'a',ufs);
                int r2=findroot(str[3]-'a',ufs);
                if(r1!=r2)
                {
                    ufs[r1]+=ufs[r2];
                    ufs[r2]=r1;
                }
            }
        }
        for(auto& str:equations)
        {
            if (str[1]=='!') //两个字母是不等关系
            {
                int r1=findroot(str[0]-'a',ufs);
                int r2=findroot(str[3]-'a',ufs);
                if(r1==r2)
                {
                    return false;
                }
            }
        }
    return true;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值