目录
一. 并查集原理
在一些应用问题中,需要将n个不同的元素划分成一些不相交的集合。
开始时,每个元素自成一个单元素集合,然后按一定的规律,将归于同一组元素的集合合并。在此过程中要反复用到查询某一个元素归属于哪个集合的运算。
适合于描述这类问题的抽象数据类型称为 并查集(union-findset)
并查集通常使用一个数组来表示:
- 数组的下标对应集合中元素的编号
- 数组中如果为负数,负号代表根,数字代表该集合中元素个数
- 数组中如果为非负数,代表该元素双亲在数组中的下标
初始化时,每个元素的根都是它自己,数组中的值均初始化为 -1,即每个元素自成一个集合。

比如 0 3 5 是一个集合,1 2 6 8 是一个集合,4 7 9 是一个集合,此时的数组:


如果将 0 1 两个集合合并:

并查集的应用场景:
- 操作系统:在处理进程同步时,判断两个进程是否属于同一个同步集合
- 图最小生成树算法:判断图中两个节点是否连通,确定最小生成树
- 社交网络分析:确定社交网络中两个人是否可以通过某种关系链条相连;找出社交网络中的集团或者社区结构。
二. 并查集实现
import java.util.Arrays;
public class UnionFindSet {
public int[] elem;//底层是一个数组
public int n;
UnionFindSet(int n){
this.n = n;
elem = new int[n];
Arrays.fill(elem, -1);//整体初始化为-1:代表根
}
//查找根
public int findParent(int index){
if(index < 0 || index >= n){
throw new ArrayIndexOutOfBoundsException("下标不合法");
}
while(elem[index] >= 0){
index = elem[index];
}
return index;
}
//判断两个数字 是不是在同一个集合当中
public boolean isSameSet(int index1, int index2){
return findParent(index1) == findParent(index2);
}
//获取集合的个数
int getSetCount(){
int count = 0;
for(int i : elem){
if(i < 0){
count++;
}
}
return count;
}
void print(){
System.out.println(Arrays.toString(elem));
}
//合并x1和x2,但是有个问题,x1和x2必须都是根;所以,先得查找x1和x2的根
public void union(int index1, int index2){
int parent1 = findParent(index1);
int parent2 = findParent(index2);
if(isSameSet(index1, index2)){
//说明x1和x2的根是相同的 不进行合并
return;
}
elem[parent1] = elem[parent2]+elem[parent1];
elem[parent2] = parent1;
}
}
三. 并查集应用(Leetcode)
等式方程的可满足性:
给定一个由表示变量之间关系的字符串方程组成的数组,每个字符串方程 equations[i] 的长度为 4,并采用两种不同的形式之一:"a==b" 或 "a!=b"。在这里,a 和 b 是小写字母(不一定不同),表示单字母变量名。只有当可以将整数分配给变量名,以便满足所有给定的方程时,才返回 true,否则返回 false。
public boolean equationsPossible(String[] equations) {
UnionFindSet unionFindSet = new UnionFindSet(26);
//必须先添加完 == 之后,再遍历一遍 判断!= 的是否在一个集合中
for(String s:equations){
if(s.charAt(1) == '='){
unionFindSet.union(s.charAt(0)-'a', s.charAt(3)-'a');
}
}
// 如果是 != 但还在同一个集合,则返回false
for(String s:equations){
if(s.charAt(1) == '!'){
if(unionFindSet.isSameSet(s.charAt(0)-'a', s.charAt(3)-'a')){
return false;
}
}
}
return true;
}
省份数量:
有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。 省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。 给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。 返回矩阵中 省份 的数量。
public int findCircleNum(int[][] isConnected) {
int n = isConnected.length;
UnionFindSet unionFindSet = new UnionFindSet(n);
for(int i = 0; i < n; i++){
for(int j = 0; j < n; j++){
//如果相连,合并到同一个集合
if(isConnected[i][j] == 1){
unionFindSet.union(i,j);
}
}
}
return unionFindSet.getSetCount();
}
1346

被折叠的 条评论
为什么被折叠?



