文章目录
一、什么是并查集
在一些应用问题中,需要将n个不同的元素划分成一些不相交的集合。开始时,每个元素自成一个单元素集合,然后按一定的规律将归于同一组元素的集合合并。在此过程中要反复用到查询某一个元素归属于那个集合的运算。适合于描述这类问题的抽象数据类型称为并查集(union-findset)。
二、并查集可以解决哪些问题
- 查找元素属于哪个集合
沿着数组表示树形关系以上一直找到根(即:树中中元素为负数的位置) - 查看两个元素是否属于同一个集合
沿着数组表示的树形关系往上一直找到树的根,如果根相同表明在同一个集合,否则不在 - 将两个集合归并成一个集合
将两个集合中的元素合并
将一个集合名称改成另一个集合的名称 - 集合的个数
遍历数组,数组中元素为负数的个数即为集合的个数。
三、实现并查集
#pragma once
#include<vector>
#include<iostream>
#include<cstdio>
using namespace std;
class UnionFindSet
{
public:
//初始化
UnionFindSet(size_t size)
:_set(size,-1)
{}
//找根
//返回值是根节点的下标
//直到找到小于0的数就是根
size_t FindRoot(int x)
{
while (_set[x] >= 0)
{
x = _set[x];
}
return x;
}
//合并
bool Union(int x, int y)
{
int root1 = FindRoot(x);
int root2 = FindRoot(y);
//是同一个根的话就没有合并的必要了
if (root1 == root2)
return false;
//我们这里选择将root2合并到root1上
//将root2的根变成root1
_set[root1] += _set[root2];
_set[root2] = root1;
}
//求集合的个数
//有几个小于0的数就有几个集合
size_t UnionCount()
{
int count = 0;
for(size_t i = 0;i<_set.size();i++)
{
if (_set[i] < 0)
{
count++;
}
}
return count;
}
private:
std::vector<int> _set;
};
void UnionFindSet_test()
{
UnionFindSet u(10);
u.Union(0, 6);
u.Union(7, 6);
u.Union(7, 8);
u.Union(1, 4);
u.Union(4, 9);
u.Union(2, 3);
u.Union(2, 5);
cout << u.UnionCount() << endl;
}
四、相关OJ练习、
1.省份数量
class Solution {
public:
int findCircleNum(vector<vector<int>>& isConnected) {
//并查集问题,只需要找出根<0的数即可
vector<int> ufs(isConnected.size(),-1);
//写一个能够求根的lambda表达式
auto FindRoot = [&ufs](int x){
while(ufs[x]>=0)
x = ufs[x];
return x;
};
for(size_t i = 0;i<isConnected.size();i++)
{
for(size_t j = 0;j<isConnected[i].size();j++)
{
if(isConnected[i][j] == 1)
{
int root1 = FindRoot(i);
int root2 = FindRoot(j);
if(root1 != root2)
{
ufs[root1]+=ufs[root2];
ufs[root2]=root1;
}
}
}
}
int n = 0;
for(auto e:ufs)
{
if(e<0)
n++;
}
return n;
}
};
2 .等式的可满足性
class Solution {
public:
bool equationsPossible(vector<string>& equations) {
vector<int> ufs(26,-1);
auto FindRoot = [&ufs](int x)
{
while(ufs[x] >= 0)
x = ufs[x];
return x;
};
//将所有=的先加入到并查集中
for(size_t i = 0;i<equations.size();i++)
{
if(equations[i][1] == '=')
{
int root1 = FindRoot(equations[i][0]-'a');
int root2 = FindRoot(equations[i][3]-'a');
if(root1!=root2)
{
ufs[root1]+=ufs[root2];
ufs[root2]=root1;
}
}
}
//将!=的比并查集中对比查看是否有重复
//有的话就不满足
for(size_t i = 0;i<equations.size();i++)
{
if(equations[i][1] == '!')
{
int root1 = FindRoot(equations[i][0]-'a');
int root2 = FindRoot(equations[i][3]-'a');
if(root1 ==root2)
return false;
}
}
return true;
}
};