一、概念
并查集主要用于解决元素分组的问题。
并指合并;查指查找;集指集合。通俗一点来说就是查找、合并集合。
对于两个不同的元素,当它们归属于一个父元素或者说拥有一个共同的祖先,我们就说这两个元素同属一个集合。
百度百科解释:并查集是一种树型的数据结构,用于处理一些不相交集合(disjoint sets)的合并及查询问题。
二、应用
并查集在图中的用途比较广泛。
- 判定无向图中是否存在环
- 合并集合
- 判定元素是否同属一个集合,统计集合个数
三、解析
例一
假定有a、b、c、d四个元素,它们的关系如下图所示,现判断元素a、c是否同属一个集合?
观察发现,元素a、c属于同一个集合,怎么判断的?
通过图我们可以获取两个信息
a ——> b ——> d c ——> d
用树的知识解释:
a的父节点是b,b的父节点是d,d是a的祖先
c的父节点是d
元素a和元素c都有一个共同的父节点d,因此元素a、元素c同属一棵树/同属一个集合。
例二
有这么一个新的元素关系,如下图所示,请问元素a、元素2是否同属一个集合?如若不是,请合并。
显然,元素a和元素2并不属于同一个集合。 那么,怎么合并呢?
我们知道,元素a的祖先是元素d,元素2的祖先是元素3,那么能否直接将元素d和元素3合并呢?
答案是肯定的。正确的操作就是将元素d挂靠到元素3下面(实际操作不会这么暴力,会进行相关优化)。
到了这里,小伙伴大概已经知道了,如何判断两个元素是否处于同一个集合/合并到同一个集合中。
解疑
大家也许会疑惑:集合哪里来的从属关系?
这就是并查集解题的巧妙之处了:在开头我们就解释了,并查集是一种树型的数据结构。
在我们利用并查集处理集合元素的过程中,选出集合中的某一个元素作为集合的代表(元素可以是集合中的任意一个),也就是其他的元素的共同祖先(树中的根节点)。这一步在初始化的代码就能体现出来,后面会讲。
四、代码
4.1 初始化
把每一个元素当做一个单独的集合,并设置父元素指向自己本身
int[] parent;
// n代表集合内元素的个数
public void init(int n){
parent = new int[n];
for (int i = 0; i < n; i++) {
// 把每一个元素当做一个单独的集合,并设置父元素指向自己本身
parent[i] = i;
}
}
4.2 查
怎样才算找到了某一个元素的父元素?
在初始化时,我们将所有元素的父元素都定义为其本身: parent[i] = i
联系并查集处理集合的方法:选取集合中的一个元素作为代表元素,通俗的说就是选取一个元素作为其他的元素的祖先(最开始集合中只有一个元素)。
说明无论怎么合并,必定会存在:x == parent[x],而这就是递归的终止条件。
public int find(int x) {
if (parent[x] == x) return x;
int parentX = parent[x];
return find(parentX);
}
思考:这个查找过程能不能进行优化?(联系我们查找的目的)
4.3 合并
合并前,必须先找到两个元素的祖先并判断是否已经在同一个集合。
/**
* 合并
* @param x 元素1
* @param y 元素2
* @return false:表示两元素已经在同一个集合,不用合并 true:合并成功
*/
public boolean merge(int x,int y) {
// 1. 找父元素
int parentX = find(x);
int parentY = find(y);
// 若两个元素的祖先相同,说明已经在同一个集合了
if (parentX == parentY) return false;
// 2. 合并
// 这里两种写法,哪一种都可以
parent[parentX] = parentY;
// parent[parentY] = parentX;
return true;
}
4.4 总代码
int[] parent;
/**
* 1. 初始化
*/
public void init(int n){
parent = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
}
}
/**
* 2. 查
*/
public int find(int x) {
if (parent[x] == x) return x;
int parentX = parent[x];
return find(parentX);
}
/**
* 3. 合并
* @param x 元素1
* @param y 元素2
* @return false:表示两元素已经在同一个集合,不用合并 true:合并成功
*/
public boolean merge(int x,int y) {
// 1. 找父元素
int parentX = find(x);
int parentY = find(y);
// 若两个元素的祖先相同,说明已经在同一个集合了
if (parentX == parentY) return false;
// 2. 合并
// 这里两种写法,哪一种都可以
parent[parentX] = parentY;
// parent[parentY] = parentX;
return true;
}
五、总结
到这里,并查集的概念以及代码模板就讲解完成了,后续会继续讲解并查集的相关优化以及有关的算法题目。
希望大家看了我的文章有所收获!
如果有写得不好的地方,欢迎大家指正!
完结撒花!!!!!!!!!