并查集原理
在一些应用问题中,需要
将
n
个不同的元素划分成一些不相交的集合
。
开始时,每个元素自成一个单元素集
合,然后按一定的规律将归于同一组元素的集合合并
。在此过程中
要反复用到查询某一个元素归属于那个集
合的运算
。适合于描述这类问题的抽象数据类型称为
并查集
(union-fifind set)
。
并查集一般可以解决一下问题:
1.
查找元素属于哪个集合
沿着数组表示树形关系以上一直找到根
(
即:树中中元素为负数的位置
)
2.
查看两个元素是否属于同一个集合
沿着数组表示的树形关系往上一直找到树的根,如果根相同表明在同一个集合,否则不在
3.
将两个集合归并成一个集合
将两个集合中的元素合并
将一个集合名称改成另一个集合的名称
4.
集合的个数
遍历数组,数组中元素为负数的个数即为集合的个数。
并查集实现
class UnionFindSet
{
public:
UnionFindSet(int n) : _ufs(n, -1) {}
//4. 集合的个数
//遍历数组,数组中元素为负数的个数即为集合的个数
int count() const
{
int count = 0;
for (int i : _ufs)
if (i < 0)
++count;
return count;
}
//1. 查找元素属于哪个集合
//沿着数组表示树形关系以上一直找到根(即:树中中元素为负数的位置)
int findRoot(int index)
{
while (_ufs[index] >= 0)
{
index = _ufs[index];
}
return index;
}
//2. 查看两个元素是否属于同一个集合
//沿着数组表示的树形关系往上一直找到树的根,如果根相同表明在同一个集合,否则不在
bool isUnion(int x1, int x2)
{
int index1 = findRoot(x1);
int index2 = findRoot(x2);
return index1 == index2;
}
//3. 将两个集合归并成一个集合
//将两个集合中的元素合并
//将一个集合名称改成另一个集合的名称
bool unionSet(int x1, int x2)
{
int index1 = findRoot(x1);
int index2 = findRoot(x2);
if (index1 == index2)
return false;
_ufs[index1] += _ufs[index2];
_ufs[index2] = index1;
return true;
}
private:
vector<int> _ufs;
};
并查集应用
1.等式方程的可满足性
链接:https://leetcode-cn.com/problems/satisfiability-of-equality-equations
给定一个由表示变量之间关系的字符串方程组成的数组,每个字符串方程 equations[i] 的长度为 4,并采用两种不同的形式之一:"a==b" 或 "a!=b"。在这里,a 和 b 是小写字母(不一定不同),表示单字母变量名。
只有当可以将整数分配给变量名,以便满足所有给定的方程时才返回 true,否则返回 false。
class Solution {
public:
bool equationsPossible(vector<string>& equations)
{
UnionFionSet us(26);
for(auto &str:equations)
if(str[1]=='=')
us.unionSet(str[0]-'a',str[3]-'a');
for(auto &str:equations)
if(str[1]=='!'&&us.isUnion(str[0]-'a',str[3]-'a'))
return false;
return true;
}
};
2.朋友圈
链接:https://leetcode-cn.com/problems/bLyHh0
一个班上有 n 个同学,其中一些彼此是朋友,另一些不是。朋友关系是可以传递的,如果 a 与 b 直接是朋友,且 b 与 c 是直接朋友,那么 a 与 c 就是间接朋友。
定义 朋友圈 就是一组直接或者间接朋友的同学集合。
给定一个 n x n 的矩阵 isConnected 表示班上的朋友关系,其中 isConnected[i][j] = 1 表示第 i 个同学和第 j 个同学是直接朋友,而 isConnected[i][j] = 0 表示二人不是直接朋友。
返回矩阵中 朋友圈的数量。
class Solution
{
public:
int findCircleNum(vector<vector<int>>& isConnected)
{
UnionFionSet us(isConnected.size());
for(int i = 0;i<isConnected.size();++i)
{
for(int j = 0;j<isConnected[i].size();++j)
{
if(i==j)
continue;
if(isConnected[i][j]==1)
us.unionSet(i,j);
}
}
return us.count();
}
};