并查集学了忘,忘了学,再学再忘,干脆写成博客,忘了就回来回顾一下。
并查集主要用于解决一些元素分组的问题。它管理一系列不相交的集合,并支持两种操作:
- 合并(Merge):把两个不相交的集合合并为一个集合。
- 查询(Find):查询两个元素是否在同一个集合中
初始化: 一开始所有节点都是独立的个体,自己是自己的父亲
int fa[MAXN];
inline void init(int n)
{
for (int i = 1; i <= n; ++i)
fa[i] = i;
}
查询:按照连接链条查上去就可以找到父亲
int find(int x)
{
if(x == fa[x])
return x;
else{
fa[x] = find(fa[x]); //父节点设为根节点
return fa[x]; //返回父节点
}
}
合并:两个集合合并,也可以看作是两棵树的根节点连在一起就可以了
inline void merge(int i, int j)
{
fa[find(i)] = find(j);
}
并查集关键点就在于上述的两个函数,其中在查找函数中我们注意到有一条指令是
fa[x] = find(fa[x]);
这条指令是优化并查集的关键,如果没有这句话,那么因为合并的原因,在极端情况下集合的形状会变为一个链条,查询效率底下。
⚠并查集参考:算法学习笔记(1) : 并查集 - 知乎 (zhihu.com)
⬆⬆⬆这篇文章详细的介绍了并查集,图文并茂的形式慢慢引导我们从合并变为链条到后面慢慢的优化压缩路径,看完这篇会对大家帮助很大!
回过头看这道题:
非常经典的并查集题目,只需要遍历一遍分好集合,记录连通分量个数即可
class Solution {
public:
int fa[201];
int findCircleNum(vector<vector<int>>& isConnected) {
for(int i = 0; i < 201; i++){
fa[i] = i;
}
int n = isConnected.size();
for(int i = 0; i < n; i++){
for(int j = i; j < n; j++){
if(isConnected[i][j] == 1){
merge(i,j);
}
}
}
int ans = 0;
for(int i = 0; i < n; i++){
if(fa[i] == i){
ans++;
}
}
return ans;
}
int find(int x){
if(fa[x] == x) return x;
fa[x] = find(fa[x]);
return fa[x];
}
void merge(int x, int y){
fa[find(x)] = find(y);
}
};