参考:https://blog.csdn.net/hebtu666/article/details/104424877/?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_title~default-0.control&spm=1001.2101.3001.4242
323. 无向图中连通分量的数目
注意:
你可以假设在 edges 中不会出现重复的边。而且由于所以的边都是无向边,[0, 1] 与 [1, 0] 相同,所以它们不会同时在 edges 中出现。
答案:
class Solution {
public int countComponents(int n, int[][] edges) {
Union u = new Union(n);
for(int[] nums : edges){
int a = nums[0] , b = nums[1];
u.marge(a,b);
}
return u.getNum(n);
}
}
class Union{
//并查集本体
int[] nums;
//记录根节点层数
int[] sign;
//初始化每个节点的祖先为自身并把每棵树层数置为1
public Union(int n){
nums = new int[n];
for(int i = 0 ; i < n ; i++){
nums[i] = i;
}
sign = new int[n];
Arrays.fill(sign , 1);
}
public int find(int x){
//未优化版本 return x == nums[x] ? nums[x] : find(x);
if(x != nums[x]){
//把每个遍历过的节点的父亲节点都置为根节点,相当于缩减了树的层数,增加了宽度
nums[x] = find(nums[x]);
}
return nums[x];
}
public void marge(int x , int y){
//查出两个节点的根节点
int fx = find(x) , fy = find(y);
if(fx == fy) return;
//比较深度,把深度小的连接到深度大的上
if(sign[fx] >= sign[fy]){
nums[fy] = fx;
}else{
nums[fx] = fy;
}
//若深度相同,则fx的深度需要+1
if(sign[fx] == sign[fy]){
sign[fx]++;
}
//提问:为什么深度不同时,作为祖先的节点深度无变化?
//答:因为优化后的并查集可以想象为一棵多叉树,深度不同进行合并时,作为被合并的树,合并后最大深度也就跟祖先树相同,所以无需变化。
}
//判断是否在一棵树中
public boolean isUnion(int x , int y){
return find(x) == find(y);
}
//对本题的具体操作函数,查询连通分量个数
public int getNum(int n){
int ans = 0;
boolean[] bl = new boolean[n];
for(int i = 0 ; i < n ; i++){
int fi = find(i);
//该树已经计算过,跳过
if(bl[fi]) continue;
ans++;
//记录根
bl[fi] = true;
}
return ans;
}
}