并查集学习
并查集学习
解释:
一个数组保留这个点所在在范围,通过一个唯一的值标识该点的范围,这个值为祖先节点pre。
判断范围时并查集会十分快。
步骤:
核心步骤在于维护这个范围数组pre[n],pre[i] = i 时表示,这个结点是这个范围的标识结点,不需要进行下一步寻找
初始化:
初始化pre[n]数组,使得每个节点为一个独立的范围,pre[i] = i,当前祖先是本身
for(int i=1;i<=n;i++) pre[i] = i;
寻找:
通过不断循环判断当前节点是否是标识节点,否则就寻找它的标识节点代表范围
int find(int x){
int root = x;
while(pre[root] != root) root = pre[root];
pre[x] = root;
return root;
}
优化:
并查集路径压缩 | 菜鸟教程 (runoob.com)
可以对它走过的路径进行优化,下次再次访问路径结点时直接找到根结点
通过再次遍历一遍路径,并对路径的范围标识赋值
int find(int x) {
// root存储根节点,p存储父节点,temp存储子节点
int p = x,root = x,temp;
while(pre[root] != root) {
root = pre[root];
}
while(pre[p] != p) {
temp = pre[p];
pre[p] = root;
p = temp;
}
return root;
}
合并:
将两个结点的范围定义为同一个,因此要将标识进行更新,对根结点进行更新
注意:合并结点时并没有对路径结点进行合并,因为只用寻找一次路径结点又会进行合并(省代码)
void merge(int x,int y){
int rx = find(x); // 寻找x的范围结点标识,根节点
int ry = find(y); // 寻找y的范围结点标识,根节点
if(rx != ry){ // 范围不一致
pre[rx] = ry; // rx结点并入ry范围,前后关系不影响思想的表达也可以pre[ry] = rx
}
}
范围判断:
if(find(x) != find(y)){
// 题目要求执行逻辑
}
例题:P3367 【模板】并查集 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
package 算法回顾.并查集.P3367_模板_并查集;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.Scanner;
public class Main {
/**
* 祖宗数组
*/
static int pre[];
/**
* 快速输入(流标识对象(缓冲流读取对象(输入流读取对象(输入流))))
*/
static StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
/**
* 快速输出(输出对象(缓冲流输出对象(输出流输出对象(输出流))))
*/
static PrintWriter pw = new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out)));
/**
* 寻找祖先结点
* @param x
* @return 祖先节点
*/
static final int find(int x) {
int p = x,root = x,temp;
while(pre[root] != root) {
root = pre[root];
}
while(pre[p] != p) {
temp = pre[p];
pre[p] = root;
p = temp;
}
return root;
}
/**
* 输入封装
* @return 获取到的整数值
* @throws Exception
*/
static final int nextInt() throws Exception {
st.nextToken();
return (int) st.nval;
}
/**
* 范围合并
* @param x
* @param y
*/
static final void merge(int x,int y) {
// 寻找祖先
int fx = find(x);
int fy = find(y);
// 不在同一个范围
if(fx != fy) {
// 合并
pre[fx] = fy;
}
}
public static void main(String[] args) throws Exception {
int n = nextInt();
int m = nextInt();
pre = new int[n+1];
// 祖宗数组初始化
for(int i=1;i<=n;i++) pre[i] = i;
// 防止GC跟不上定义,定义放在逻辑外面
int s,x,y;
for(int i=1;i<=m;i++) {
// int s,x,y;
//输入数据
s = nextInt();
x = nextInt();
y = nextInt();
// 选择
if(s == 1) {
merge(x,y);
}
// 输出
else pw.println(find(x) == find(y)?"Y":"N");
}
// 清空输出缓存区
pw.flush();
}
}

浙公网安备 33010602011771号