引言
并查集是用于将n个不同元素分成一组不相交集合的数据结构
实现
带路径压缩和按秩合并的并查集
在实现上,数组中的每一个值的绝对值代表当前集合的元素个数
两个操作的时间复杂度几乎为常数
class ufs{
private:
int* father;
public:
ufs(int size)
{
father = new int[size];
for(int i = 0;i < size;i++)//这里的设计很精妙
father[i] = -1; //用正数会带来混乱,数组绝对值为该集合个数
}
int find(int w)
{
if(father[w] < 0) //找到根
return w;
return father[w] = find(father[w]); //路径压缩
//迭代实现
/*while(father[w] >= 0)
{
father[w] = father[father[w]];
w = father[w];
}
return w;*/
}
void _union(int w,int v)
{
int fw = find(w);
int fv = find(v);
if(fw == fv)//属于同一个集合
return ;
//按秩合并
//这里看绝对值
if(father[fw] > father[fv])//集合fw的个数小于fv
{
father[fv] += father[fw];//小的加到大的上面
father[fw] = fv;
}
else
{
father[fw] += father[fv];
father[fv] = fw;
}
}
};
下面给出以朋友圈展现并查集这个非常精妙的数据结构
这道题,是很典型的并查集,寻找不相交集合的个数。在上面ufs的基础上增加一个变量成员num,用于记录当前不相交集合的个数。每合并一次少一个。实际上我们只遍历了1/2(n2-n)个元素
class ufs{
private:
int* father;
public:
int num;
ufs(int size)
{
num = size;
father = new int[size];
for(int i = 0;i < size;i++)
father[i] = -1; //用正数会带来混乱,数组绝对值为该集合个数
}
int find(int w)
{
if(father[w] < 0) //找到根
return w;
return father[w] = find(father[w]); //路径压缩
}
void _union(int w,int v)
{
int fw = find(w);
int fv = find(v);
if(fw == fv)//属于同一个集合
return ;
//按秩合并
//这里看绝对值
if(father[fw] > father[fv])//集合fw的个数小于fv
{
father[fv] += father[fw];//小的加到大的上面
father[fw] = fv;
}
else
{
father[fw] += father[fv];
father[fv] = fw;
}
num--;
return;
}
};
class Solution {
public:
int findCircleNum(vector<vector<int>>& M) {
ufs s(M.size());
for(int i = 0;i < M.size();i++) //这里我们只需要遍历下三角
for(int j = 0;j <= i;j++)
if(M[i][j] && i!=j)
s._union(i,j);
return s.num;
}
};
int main()
{
Solution s;
vector<vector<int>> M = {{1,0,0,1},{0,1,1,0},{0,1,1,1},{1,0,1,1}};
cout<<s.findCircleNum(M);
}