【无标题】

并查集(Disjoint Set Union)详解:原理、优化与应用

并查集(也称为不相交集合数据结构 ,英文 Disjoint Set Union 或 Union-Find)是一种非常高效的数据结构,主要用于处理动态连通性问题。它支持两种核心操作:

  • 查询(Find):判断两个元素是否属于同一个集合(即是否连通)。
  • 合并(Union):将两个集合合并成一个。

并查集在算法竞赛(如 OI、ACM)和实际工程中应用广泛,例如最小生成树(Kruskal 算法)、图的连通分量统计、网格连通问题等。其最大特点是操作几乎达到 O(1) 时间复杂度(摊销后)。

本文将从基本原理入手,逐步介绍实现、优化技巧,并附上完整代码示例。

1. 并查集的基本原理

并查集用一个森林 (多棵树)来表示多个不相交的集合。每棵树代表一个集合,树的根节点作为该集合的代表元。

  • 用一个数组 parent[] 表示每个节点的父节点。
  • 初始时,每个元素都是独立的集合:parent[i] = i
  • 查找根节点(Find 操作):从某个节点向上追溯,直到找到父节点为自己本身的节点(根)。
  • 合并(Union 操作):找到两个元素的根节点,如果不同,则将一棵树的根指向另一棵树的根。

简单实现(无优化)

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 100010;
int parent[N];

void init(int n) {
    for (int i = 1; i <= n; i++) {
        parent[i] = i;
    }
}

int find(int x) {
    if (parent[x] == x) return x;
    return find(parent[x]);  // 递归查找根
}

void union_sets(int x, int y) {
    int rx = find(x);
    int ry = find(y);
    if (rx != ry) {
        parent[rx] = ry;  // 将 rx 的根指向 ry 的根
    }
}

这种实现最坏情况下树会退化成一条链(高度为 n),每次 Find 操作可能需要 O(n) 时间。

2. 优化一:路径压缩(Path Compression)

路径压缩的核心思想是:在查找根节点的过程中,将路径上的所有节点直接指向根节点,从而压扁树的高度。

修改 Find 函数:

cpp 复制代码
int find(int x) {
    if (parent[x] != x) {
        parent[x] = find(parent[x]);  // 递归压缩路径
    }
    return parent[x];
}

效果示意:

  • 未压缩:1 → 2 → 3 → 4(根)
  • 压缩后:1、2、3 都直接指向 4

路径压缩后,树的高度会大幅降低,后续查询极快。

3. 优化二:按秩合并(Union by Rank)

单纯路径压缩虽然优秀,但合并时如果总是随意连接,可能导致树偏斜。按秩合并通过维护一个 rank[] 数组记录每棵树的"秩"(近似高度)来优化。

  • 初始时,所有秩为 1(或 0)。
  • 合并时,总是将秩小的树挂到秩大的树上
  • 如果秩相等,任选一个挂接,并将胜出方的秩 +1。
cpp 复制代码
int rank[N];

void init(int n) {
    for (int i = 1; i <= n; i++) {
        parent[i] = i;
        rank[i] = 1;  // 初始秩为 1
    }
}

void union_sets(int x, int y) {
    int rx = find(x);
    int ry = find(y);
    if (rx == ry) return;
    // 将秩小的合并到秩大的分支上
    if (rank[rx] < rank[ry]) {
        parent[rx] = ry;
    } else {
        parent[ry] = rx;
        if (rank[rx] == rank[ry]) {
            rank[rx]++;
        }
    }
}

路径压缩 + 按秩合并 是标准优化组合,摊销时间复杂度为 O(α(n)),其中 α(n) 是反阿克曼函数,增长极慢,实际可视为常数。

4. 完整优化版代码

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;

const int N = 100010;
int parent[N];
int rank[N];

void init(int n) {
    for (int i = 1; i <= n; i++) {
        parent[i] = i;
        rank[i] = 1;
    }
}

int find(int x) {
    if (parent[x] != x) {
        parent[x] = find(parent[x]);  // 路径压缩
    }
    return parent[x];
}

void union_sets(int x, int y) {
    int rx = find(x);
    int ry = find(y);
    if (rx == ry) return;
    
    if (rank[rx] < rank[ry]) {
        swap(rx, ry);  // 保证 rx 的秩更大
    }
    parent[ry] = rx;
    if (rank[rx] == rank[ry]) {
        rank[rx]++;
    }
}

int main() {
    // 示例使用
    init(10);
    union_sets(1, 2);
    union_sets(3, 4);
    union_sets(2, 4);
    cout << (find(1) == find(4) ? "连通" : "不连通") << endl;  // 输出:连通
    return 0;
}

5. 常见应用场景

  1. 图的连通分量统计:遍历所有边进行 Union,最后统计根节点数量。
  2. Kruskal 最小生成树:按边权排序,Union 时判断是否形成环。
  3. 网格/迷宫连通问题:如 LeetCode 的"朋友圈"、"被围绕的区域"等。
  4. 在线判断连通性:动态添加边,实时查询是否连通。

6. 扩展:按大小合并(Union by Size)

另一种优化是维护每个集合的大小 size[],合并时将小集合挂到大集合上。效果与按秩合并类似,有时更优(尤其在需要统计集合大小时)。

cpp 复制代码
int size[N];

void init(int n) {
    for (int i = 1; i <= n; i++) {
        parent[i] = i;
        size[i] = 1;
    }
}

void union_sets(int x, int y) {
    int rx = find(x), ry = find(y);
    if (rx == ry) return;
    if (size[rx] < size[ry]) swap(rx, ry);
    parent[ry] = rx;
    size[rx] += size[ry];
}

总结

并查集是算法竞赛中的"神器"之一,实现简单却效率极高。记住两大优化:

  • 路径压缩:让 Find 更快。
  • 按秩/大小合并:防止树退化。

掌握了并查集,很多图论和连通性问题都会迎刃而解。建议多刷相关题目(如 HDU 1232 畅通工程、POJ 2236 等)来加深理解。

相关推荐
shughui15 小时前
Python基础面试题:语言定位+数据类型+核心操作+算法实战(含代码实例)
开发语言·python·算法
王老师青少年编程16 小时前
信奥赛C++提高组csp-s之拓扑排序详解
c++·算法·拓扑排序·csp·信奥赛·csp-s·提高组
No0d1es16 小时前
2025年12月电子学会青少年软件编程Python六级等级考试真题试卷
开发语言·python·青少年编程·等级考试·电子学会
zlp199216 小时前
xxl-job java.sql.SQLException: interrupt问题排查(二)
java·开发语言
superman超哥16 小时前
Rust HashSet与BTreeSet的实现细节:集合类型的底层逻辑
开发语言·后端·rust·编程语言·rust hashset·rust btreeset·集合类型
浩瀚地学16 小时前
【Java】异常
java·开发语言·经验分享·笔记·学习
kaikaile199516 小时前
matlab计算流场
人工智能·算法·matlab
张np16 小时前
java基础-LinkedHashMap
java·开发语言
gihigo199816 小时前
基于MATLAB的周期方波与扫频信号生成实现(支持参数动态调整)
开发语言·matlab