并查集

并查集原理

在一些有N个元素的集合应用问题中,需要将N个不同的元素划分成一些不相交的集合。开始时,每一个元素自成一个单元素集合,然后按照一定的规律将归于同一组元素的集合合并。其间要反复查找一个元素在哪个集合中。适用于描述这一类问题的抽象数据类型称为并查集(Union find set)。

实际上,并查集的存储是一个森林,每一个集合是一个树。
在这里插入图片描述
观察数组融合规律,得出以下结论:

  1. 数组的下标对应集合中元素的编号
  2. 数组中如果为负数,负数代表根,数字代表该集合中的元素个数
  3. 数组中如果为负数,代表元素双亲在数组中的下标

在这里插入图片描述
通过以上例子可知,并查集一般可以解决以下问题:

  1. 查找元素属于那个集合
    数组存放的是父节点,一直往上可以找到根
  2. 查看两个元素是否属于同一个集合
    查看两个元素的根节点是否相同,相同则在同一个集合
  3. 将两个集合归并成一个集合
  4. 集合的个数
    遍历数组,查看元素为负数的个数即为集合的个数

并查集实现

class UnionFindSet {
public :
	UnionFindSet(int n) {
		_v.resize(n,-1);
	}

	int FindRoot(int x) {
		while (_v[x] >= 0) {
			x = _v[x];
		}
		return x;
	}

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

		// x1 和 x2 的根相同,本就在同一个集合中
		if (root1 == root2)
			return false;

		_v[root1] += _v[root2];
		_v[root2] = root1;
		return true;
	}
private:
	vector<int> _v;
};

应用

朋友圈_leetcode_547

class Solution {
public:
    class UnionFindSet {
public :
	UnionFindSet(int n) {
		_v.resize(n,-1);
	}

	int FindRoot(int x) {
		while (_v[x] >= 0) {
			x = _v[x];
		}
		return x;
	}

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

		// x1 和 x2 的根相同,本就在同一个集合中
		if (root1 == root2)
			return false;

		_v[root1] += _v[root2];
		_v[root2] = root1;
		return true;
	}

    int Size() {
        int n = 0;
        for(int e : _v) {
            if(e < 0)
                ++n;
        }
        return n;
    }

private:
	vector<int> _v;
};
    int findCircleNum(vector<vector<int>>& M) {
        UnionFindSet ufs(M.size());
        
        for(int i = 0; i < M.size(); ++i) {
            for(int j = 0; j < M[i].size(); ++j) {
                if(M[i][j] == 1)
                    ufs.Union(i,j);
            }
        }

        return ufs.Size();
    }
};

等式方程的可满足性_leetcode_990

class UnionFindSet {
public :
	UnionFindSet(int n) {
		_v.resize(n,-1);
	}

	int FindRoot(int x) {
		while (_v[x] >= 0) {
			x = _v[x];
		}
		return x;
	}

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

		// x1 和 x2 的根相同,本就在同一个集合中
		if (root1 == root2)
			return false;

		_v[root1] += _v[root2];
		_v[root2] = root1;
		return true;
	}
private:
	vector<int> _v;
};
class Solution {
public:
    bool equationsPossible(vector<string>& equations) {
        // 无法确定大小,但是小写字母只有26个
        UnionFindSet ufs(26);
        
        // 先遍历一遍,将相等的,先放进同一个集合
        for(auto& str : equations) {
            if(str[1] == '=') {
                // 数组大小只有 26 ,所以存相对位置,减去字符 a
                ufs.Union(str[0] - 'a',str[3] - 'a');
            }
        }

        for(auto& str : equations) {
            // 如果在同一个集合,但是两个字母又不相等,就返回 false
            if(str[1] == '!') {
                if(ufs.FindRoot(str[0] - 'a')
                == ufs.FindRoot(str[3] - 'a'))
                return false;
            }
        }

        // 没有返回 false 说明都相等,没有在一个集合但又不相等的
        return true;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值