056并查集
1.一开始元素拥有自己的集合,在自己的集合里只有这个元素自己
2.find(i):查找i所在集合的代表元素,代表元素来代表i所在的集合
3.boolean isSameSet(a,b):判断a,b在不在一个集合里
4.void union(a,b):a所在集合所有元素和b所在集合所有元素 合并成一个集合
5.各种操作单次调用的均摊时间复杂度O(1)
find一直往上找 一直找到不能再往上的
优化一 union小的去挂大的
优化二 扁平化(路径压缩)
比如说 2-3 3-4 4-5
find(2)的时候 在找父节点的过程中 我们把沿途的都给直接挂到5
那越调用速度越快,挺好
father 和 size的改变
只有代表节点的size信息有用 也就是最上方的节点对吧
father指向父节点
import java.io.*;
public class Main {
// n 和 m 为题目给定的总数和操作数
static int n, m;
// father[i] 存储 i 的父节点
static int[] father;
// size[i] 存储以 i 为根的集合的大小
static int[] size;
// 用于路径压缩的栈(临时存储路径上的节点)
static int[] stack;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] line = br.readLine().split(" ");
n = Integer.parseInt(line[0]);
m = Integer.parseInt(line[1]);
// 初始化并查集
father = new int[n + 1];
size = new int[n + 1];
stack = new int[n + 1];
build();
// 处理 m 个操作
for (int i = 0; i < m; i++) {
line = br.readLine().split(" ");
String op = line[0];
int a = Integer.parseInt(line[1]);
int b = Integer.parseInt(line[2]);
if (op.equals("M")) {
union(a, b);
} else if (op.equals("Q")) {
if (isSameSet(a, b)) {
System.out.println("Yes");
} else {
System.out.println("No");
}
}
}
br.close();
}
/**
* 初始化并查集
* 每个元素一开始都是一个独立的集合,其父节点是自身,集合大小为1。
*/
public static void build() {
for (int i = 1; i <= n; i++) {
father[i] = i;
size[i] = 1;
}
}
/**
* 查找根节点并进行路径压缩
* @param i 待查找的元素
* @return 元素 i 所在集合的根节点
*/
public static int find(int i) {
int tempSize = 0;
// 向上遍历,直到找到根节点(父节点是自身)
while (i != father[i]) {
// 将路径上的节点存入栈中
stack[tempSize++] = i;
i = father[i];
}
// 路径压缩:将栈中的所有节点直接连接到根节点 i
while (tempSize > 0) {
father[stack[--tempSize]] = i;
}
return i;
}
/**
* 判断两个元素是否在同一个集合中
* @param x 元素 x
* @param y 元素 y
* @return 如果 x 和 y 的根节点相同,则返回 true
*/
public static boolean isSameSet(int x, int y) {
return find(x) == find(y);
}
/**
* 合并两个元素所在的集合(按大小合并)
* @param x 元素 x
* @param y 元素 y
*/
public static void union(int x, int y) {
int fx = find(x); // 找到 x 的根节点
int fy = find(y); // 找到 y 的根节点
if (fx != fy) { // 如果根节点不同,说明不在同一个集合,可以合并
// 按大小合并:将小集合合并到大集合上
if (size[fx] >= size[fy]) {
size[fx] += size[fy]; // 更新大集合的大小
father[fy] = fx; // 将小集合的根节点连接到大集合的根节点
} else {
size[fy] += size[fx];
father[fx] = fy;
}
}
}
}
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 言和和和!
评论
