我们利用上一篇文章里面模拟实现的并查集来写这道题:并查集理论篇 - 掘金 (juejin.cn)
思想
首先就是判断是否i,j两个城市是否连痛,如果连通说明处于可以合并为一个集合。
class UnionFindSet
{
public:
UnionFindSet( size_t n)
:_ufs(n, -1)
{}
void Union(int x1,int x2)//合并集合
{
int root1 = Findroot(x1);
int root2 = Findroot(x2);
if (root1 == root2)return;//如果根相同,说明是一个根,即在一个集合,无需再合并
else
{
_ufs[root1] += _ufs[root2];//根加上孩子节点的编号
_ufs[root2] = root1;//孩子节点存储父节点的下标,以便找到父节点
}
}
int Findroot(int x)//找根节点
{
int parent = x;
while (_ufs[parent] >= 0)//说明不是根
{
parent = _ufs[parent];//跳到x的编号的存的下标的位置
}
return parent;
}
bool Inser(int x1,int x2)//判断a,b集合是否在一个集合
{
return Findroot(x1) == Findroot(x2);
}
size_t Setsize()//判断一个并查集有多少个集合
{
size_t size = 0;
for (size_t i = 0; i < _ufs.size(); i++)
{
if (_ufs[i] < 0)size++;//只要根节点的编号才为负值
}
return size;
}
private:
vector<int> _ufs;//编号找人
};
class Solution {
public:
int findCircleNum(vector<vector<int>>& isConnected) {
UnionFindSet ufs(isConnected.size());//初始化isConnected.size()个元素,在我们的上面实现的并查集里全部初始化为-1了,即刚开始都是独立的集合
//遍历整个isConnected数组
for(size_t i=0;i<isConnected.size();i++)
{
for(size_t j=0;j<isConnected.size();j++)
{
if(isConnected[i][j]==1)ufs.Union(i,j);//如果可以连通就合并为一个集合
}
}
return ufs.Setsize();//返回集合的个数
}
};
第二种
class Solution {
public:
int findCircleNum(vector<vector<int>>& isConnected) {
vector<int> ufs(isConnected.size(),-1);//初始化为-1,表示独立集合
//找根
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.size();j++)
{
if(isConnected[i][j]==1)
{
int root1=findRoot(i);
int root2=findRoot(j);
if(root1==root2)continue;
else
{
ufs[root1]+=ufs[root2];
ufs[root2]=root1;
}
}
}
}
int n=0;
for(auto e:ufs)
{
if(e<0)n++;//小于0说明未合并,是独立集合
}
return n;//返回独立集合的个数
}
};