最重要的问题是 什么是并查集 为什么要用并查集 怎么用 怎么优化?
讲解链接:并查集理论基础 | 代码随想录
只听过并查集 但是从来不理解他做什么 最近学了集合的内容有点感触了
并查集(Disjoint Set Union,DSU) 是一种用于管理元素分组的数据结构,主要支持以下两种操作:
-
查找(Find):确定某个元素属于哪个集合(通常返回集合的代表元素)。
-
合并(Union):将两个集合合并为一个集合。
核心思想:
-
每个集合用一棵树表示,树的根节点是集合的代表元素。
-
通过路径压缩和按秩合并优化,使操作的时间复杂度接近常数。
并查集最基础的路径压缩模版(java)
public class UnionFind {
private int[] father; // 父节点数组
// 初始化并查集
public UnionFind(int n) {
father = new int[n];
for (int i = 0; i < n; i++) {
father[i] = i; // 每个元素的父节点初始指向自己
}
}
// 查找(带路径压缩)
public int find(int u) {
return u == father[u] ? u : (father[u] = find(father[u])); // 路径压缩
}
// 判断 u 和 v 是否属于同一个集合
public boolean isSame(int u, int v) {
u = find(u);
v = find(v);
return u == v;
}
// 合并 u 和 v 所在的集合
public void join(int u, int v) {
u = find(u); // 找到 u 的根
v = find(v); // 找到 v 的根
if (u == v) return; // 如果根相同,说明已经在同一个集合,直接返回
father[v] = u; // 将 v 的根指向 u 的根
}
}
按秩合并模版
public class UnionFind {
private int[] father; // 父节点数组
private int[] rank; // 树的秩(深度)
// 初始化并查集
public UnionFind(int n) {
father = new int[n];
rank = new int[n];
for (int i = 0; i < n; i++) {
father[i] = i; // 每个元素的父节点初始指向自己
rank[i] = 1; // 初始深度为 1
}
}
// 查找(带路径压缩)
public int find(int u) {
return u == father[u] ? u : (father[u] = find(father[u])); // 路径压缩
}
// 判断 u 和 v 是否属于同一个集合
public boolean isSame(int u, int v) {
u = find(u);
v = find(v);
return u == v;
}
// 合并 u 和 v 所在的集合(带按秩合并)
public void join(int u, int v) {
u = find(u); // 找到 u 的根
v = find(v); // 找到 v 的根
if (u == v) return; // 如果根相同,说明已经在同一个集合,直接返回
// 按秩合并
if (rank[u] > rank[v]) {
father[v] = u;
} else if (rank[u] < rank[v]) {
father[u] = v;
} else {
father[v] = u;
rank[u]++;
}
}
}
并查集可以解决什么问题呢?
主要就是集合问题,两个节点在不在一个集合,也可以将两个节点添加到一个集合中。
107. 寻找存在的路径
题目链接:卡码网题目链接(ACM模式) (opens new window)
讲解链接:代码随想录
import java.util.Scanner;
public class Carlcode107 {
//并查集模版
static class DisJoint{
private int[] father;
public DisJoint(int n){
father = new int[n];//初始化 father 数组,大小为 n。
for (int i = 0; i < n; i++) {
father[i] = i;
}
}
// 查找
public int find(int n){
//如果 n 的父节点是它自己(即 n == father[n]),
//说明 n 是根节点,直接返回 n
//否则,递归查找 father[n] 的根节点,并通过路径压缩优化,
//将 n 的父节点直接指向根节点(father[n] = find(father[n]))。
return n == father[n] ? n : (father[n] = find(father[n]));
}
//合并
public void join(int n, int m){
n = find(n);//首先,通过 find 方法找到 n 和 m 的根节点。
m = find(m);
if(n == m) return;
//如果n和m的根节点相同 说明它们已经在同一个集合中,直接返回。
father[m] = n;
//否则 将m的根节点指向n的根节点(father[m] = n)
}
public boolean isSame(int n, int m){
n = find(n);
//isSame 方法用于判断节点 n 和 m 是否属于同一个集合。
m = find(m);
//通过 find 方法找到 n 和 m 的根节点。
return n == m;
}
}
public static void main(String[] args) {
int n, m;
Scanner scanner = new Scanner(System.in);
n = scanner.nextInt();
m = scanner.nextInt();
DisJoint disJoint = new DisJoint(n + 1);
//合并到一个集合里(这用的数组)
for (int i = 0; i < m; i++) {
disJoint.join(scanner.nextInt(),scanner.nextInt());
}
if(disJoint.isSame(scanner.nextInt(), scanner.nextInt())){
System.out.println(1);
}else{
System.out.println(0);
}
}
}
打卡打卡