前言
连通分量是图的基本概念,求解连通分量与并查集紧密关联。并查集,第一步需要设置并查集数据结构数组并初始化,第二步,需要用到该数组进行两节点的合并union,第三步,合并又需要先找到两节点的根节点,才知道能否合并,所以需要findFather进行递归寻找。
一、省份数量
二、并查集
package everyday;
// 省份数量。
public class FindCircleNum {
/*
target:n个城市,一个省份的全部城市直接或间接相连。请问有多少省份?
问题显然转化成联通分量的个数,而涉及到联通分量,就不得不提并查集了。
采用并查集把相应的城市并再一起,没并一次n-1,最终得到省份数量。
*/
public int findCircleNum(int[][] isConnected) {
// 记录共有多少城市
int n = isConnected.length;
// 并查集数据结构。
int[] fathers = new int[n];
// 初始化并查集数据结构。
for (int i = 0; i < n; i++) fathers[i] = i;
// 记录剩余城市数量。
int rs = n;
// 开始循环合并。
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
// 两个城市需要合并。
if (isConnected[i][j] == 1 && union(fathers, i, j)) --rs;
}
}
return rs;
}
// 合并两个节点,但是需要先找到两节点的根节点。
private boolean union(int[] fathers, int i, int j) {
int root = findFather(fathers, i);
int node = findFather(fathers, j);
// 两个有相同的根节点,本身就在一起,无法合并。
if (root == node) return false;
// 有不同的根节点,合并两者。
fathers[node] = root;
// 合并成功。
return true;
}
// 找一个节点的根节点,便于判断是否在一颗树上,且便于合并。
private int findFather(int[] fathers, int i) {
/*
// 递归找到根节点。
if (fathers[i] == i) return fathers[i];
// 递归寻找,并回溯将树铺平,即将路径上的每个节点都指向根几点。
// 好处:1-代码可改写;2-简洁;3-下一次访问根节点能不用递归太多层。
fathers[i] = findFather(fathers, fathers[i]);
// 返回根节点。
return fathers[i];
*/
// 改写。
if (fathers[i] != i) fathers[i] = findFather(fathers, fathers[i]);
return fathers[i];
}
}
总结
1)并查集三大步,定义并初始化数组,union,findFather.
2)连通分量紧密相关并查集。
参考文献
[1] LeetCode 省份数量