并查集
- 介绍并查集基本概念
- 实现并查集基本算法
- 实现的算法中采用了两种优化
- 路径压缩:在 find 操作中,通过将路径上所有节点直接连接到根节点,极大地减少了树的高度,从而优化了后续操作的时间复杂度。
- 按大小合并:在 union 操作中,将较小集合的根节点连接到较大集合的根节点,从而保持树的平衡,避免退化成链表。
并查集基本概念
并查集(Union-Find)是一种数据结构,用于处理不相交集合(Disjoint Sets)的合并和查询操作。它非常适合解决动态连通性问题,例如在图论中判断两个节点是否属于同一个连通分量。并查集的主要操作有两个:查找(Find)和合并(Union)。
核心操作
-
查找(Find):
- 查找元素所属集合的代表(根节点)。这一操作通常用于判断两个元素是否属于同一个集合。
- 使用路径压缩优化:在查找的过程中,将路径上的所有节点直接连接到根节点,从而降低树的高度,提高后续操作的效率。
-
合并(Union):
- 将两个不相交的集合合并为一个集合。
- 使用按秩合并(union by rank)或按大小合并(union by size)优化:将较小集合的根节点连接到较大集合的根节点,保持树的平衡,避免退化成链表。
实现细节
并查集通常使用一个数组来实现,数组中的每个元素表示一个节点,值表示该节点的父节点。如果某个元素是根节点,则其值为负数,表示集合的大小(按大小合并)或秩(按秩合并)。
主要方法
- 初始化:创建一个并查集,其中每个元素单独作为一个集合。
- 查找(Find):找到元素所属集合的根节点,并进行路径压缩。
- 合并(Union):将两个集合合并,并进行按秩或按大小合并。
- 连接检查(isConnected):检查两个元素是否在同一个集合中。
- 集合大小(sizeOf):返回某个元素所属集合的大小。
代码示例
以下是一个典型的并查集实现,包含路径压缩和按大小合并:
package union;
public class UnionFind {
private int[] parent;
// Creates a UnionFind data structure holding n vertices. Initially, all vertices are in disjoint sets.
public UnionFind(int n) {
parent = new int[n];
for (int i = 0; i < n; i++) {
parent[i] = -1;
}
}
// Throws an exception if v1 is not a valid index.
public void validate(int v1) {
if (v1 < 0 || v1 >= parent.length) {
throw new IndexOutOfBoundsException("Index " + v1 + " is not valid.");
}
}
// Returns the size of the set v1 belongs to
public int sizeOf(int v1) {
validate(v1);
int root = find(v1);
return -parent[root];
}
// Returns the parent of v1. If v1 is the root of a tree, returns the negative size of the tree for which v1 is the root.
public int parent(int v1) {
validate(v1);
return parent[v1];
}
// Returns true if nodes v1 and v2 are connected.
public boolean isConnected(int v1, int v2) {
validate(v1);
validate(v2);
return find(v1) == find(v2);
}
/**
* Connects two elements v1 and v2 together. v1 and v2 can be any valid elements, and a union-by-size heuristic is used.
* If the sizes of the sets are equal, tie break by connecting v1’s root to v2’s root.
* Unioning a vertex with itself or vertices that are already connected should not change the sets,
* but it may alter the internal structure of the data structure.
*/
public void union(int v1, int v2) {
validate(v1);
validate(v2);
int root1 = find(v1);
int root2 = find(v2);
if (root1 == root2) {
return; // v1 and v2 are already in the same set
}
int size1 = -parent[root1];
int size2 = -parent[root2];
if (size1 <= size2) {
parent[root1] = root2;
parent[root2] = -(size1 + size2);
} else {
parent[root2] = root1;
parent[root1] = -(size1 + size2);
}
}
// Returns the root of the set v1 belongs to. Path-compression is employed allowing for fast search-time.
public int find(int v1) {
validate(v1);
if (parent[v1] < 0) {
return v1;
}
parent[v1] = find(parent[v1]); // Path compression
return parent[v1];
}
}
并查集的应用
并查集广泛应用于图论和网络连接等领域,包括:
- 判断图中是否存在环。
- 查找最小生成树(Kruskal算法)。
- 网络连接问题。
- 社交网络中的社团检测。
并查集以其高效的操作和易于实现的特性,成为解决连通性问题的常用工具。