1.引言
在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。
并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。
先在前面给出c++代码实现:
class UnionFindSet
{
private:
vector<int>_set;
public:
UnionFindSet(int n){
_set.resize(n, -1);
}
size_t Find(int x){ //找根
while (_set[x] >= 0){
x = _set[x];
}
return x;
}
void Union(int x1, int x2){
//归并:判断两个元素是否在同一个集合
size_t root1 = Find(x1);
size_t root2 = Find(x1);
if (root1 != root2){
//需要归并
_set[root1] += _set[root2];
_set[root2] = root1;
}
}
size_t Count(){
size_t count = 0;
for (auto e : _set){
if (e < 0)
++count;
}
return count;
}
};//end of UnionFindSet;
2.并查集的使用:
1.将n个元素进行编号 给定一维数组并初始化 -1.
数字1:代表每个集合只有一个人
负号 :代表这个数据项是一个根元素
2.按照规则来合并集合
.合并
void Union(int x1, int x2){
//归并:判断两个元素是否在同一个集合
size_t root1 = Find(x1);
size_t root2 = Find(x1);
if (root1 != root2){
//需要归并
_set[root1] += _set[root2];
_set[root2] = root1;
}
//如果两个元素的根是一样的,那么不用操作;
//否则的话合并,以第一个元素为根,更新_set [ root2 ]的值 ;更新第一个根的数据个数。
.查找
size_t Find(int x){ //找根
while (_set[x] >= 0){//根的判断是,数据值为负数
x = _set[x];
}
return x; //返回下标
3.应用:
1.leetcode547—朋友圈
class UnionFindSet{
private:
vector<int>_set;
public:
UnionFindSet(int n)
{
_set.resize(n,-1);
}
size_t Find(int x){
while(_set[x]>0){
x=_set[x];
}
return x;
}
void Union(int x,int y){
size_t root1=Find(x);
size_t root2=Find(y);
if(root1 != root2){
_set[root1]+=_set[root2];
_set[root2]=root1;
}
}
size_t Count(){
size_t count=0;
for(auto e:_set)
{
if(e<0)
++count;
}
return count;
}
};
class Solution {
public:
int findCircleNum(vector<vector<int>>& M) {
int len=M.size();
UnionFindSet u1(len);
for(int i=0;i<len;++i){
for(int j=0;j<len;++j){
if(i!=j && M[i][j]==1)
u1.Union(i,j);
}
}
return u1.Count();
}
};
2.leetcode990—等式方式的可满足性 :等号两边的元素进行归并,遇到!=判断元素是否在一起
//1.将所有==两侧的元素归并
//2.遍历,检测‘!=’两侧的元素是否在同一集合