并查集(Union-Find)数据结构详解

文章目录

1. 什么是并查集?

并查集(Union-Find) 是一种用于管理 不相交集合(Disjoint Sets) 的数据结构,主要支持以下两种操作:

  • Find(u):查找元素 u 所属的集合(通常返回其根节点)。
  • Union(u, v):合并两个元素 uv 所在的集合。

并查集广泛应用于:

  • 图的连通性问题(如Kruskal算法求最小生成树)。
  • 动态连通性问题(如社交网络中的好友关系)。
  • 游戏开发(如像素连通性检测)。

2. 并查集的实现

(1)基本实现

初始时,每个元素独立构成一个集合,用数组 parent[] 存储父节点:

cpp 复制代码
vector<int> parent(n);
for (int i = 0; i < n; i++) {
    parent[i] = i; // 初始化,每个节点的父节点是自己
}
  • Find(u)(路径压缩优化):

    cpp 复制代码
    int find(int u) {
        if (parent[u] != u) {
            parent[u] = find(parent[u]); // 路径压缩
        }
        return parent[u];
    }
  • Union(u, v)(按秩合并优化):

    cpp 复制代码
    vector<int> rank(n, 0); // 记录树的深度
    bool unionSets(int u, int v) {
        int rootU = find(u);
        int rootV = find(v);
        if (rootU == rootV) return false; // 已经在同一集合
        if (rank[rootU] > rank[rootV]) {
            parent[rootV] = rootU; // 小树合并到大树
        } else if (rank[rootU] < rank[rootV]) {
            parent[rootU] = rootV;
        } else {
            parent[rootV] = rootU;
            rank[rootU]++; // 深度相同,合并后深度+1
        }
        return true;
    }

(2)优化策略

优化方法 作用 时间复杂度
路径压缩(Path Compression) 使 Find 操作接近 O ( 1 ) O(1) O(1) O ( α ( n ) ) O(α(n)) O(α(n))
按秩合并(Union by Rank) 避免树过高,保持平衡 O ( α ( n ) ) O(α(n)) O(α(n))

其中,α(n) 是 反阿克曼函数,增长极其缓慢,可以认为是常数时间。

3. 并查集的应用

(1)检测无向图的环

cpp 复制代码
bool hasCycle(vector<vector<int>>& edges, int n) {
    UnionFind uf(n);
    for (auto& edge : edges) {
        int u = edge[0], v = edge[1];
        if (!uf.unionSets(u, v)) {
            return true; // 已经连通,说明有环
        }
    }
    return false;
}

(2)Kruskal算法求最小生成树(MST)

⭐算法OJ⭐连接所有点的最小费用【最小生成树】(C++实现)Min Cost to Connect All Points

cpp 复制代码
int kruskalMST(vector<vector<int>>& edges, int n) {
    sort(edges.begin(), edges.end()); // 按权重排序
    UnionFind uf(n);
    int res = 0;
    for (auto& edge : edges) {
        int cost = edge[0], u = edge[1], v = edge[2];
        if (uf.unionSets(u, v)) {
            res += cost;
        }
    }
    return res;
}

(3)朋友圈问题

cpp 复制代码
int findCircleNum(vector<vector<int>>& isConnected) {
    int n = isConnected.size();
    UnionFind uf(n);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (isConnected[i][j]) {
                uf.unionSets(i, j);
            }
        }
    }
    int circles = 0;
    for (int i = 0; i < n; i++) {
        if (uf.find(i) == i) circles++; // 统计根节点数量
    }
    return circles;
}

4. 并查集的复杂度分析

操作 无优化 路径压缩 路径压缩 + 按秩合并
Find O ( n ) O(n) O(n) O ( α ( n ) ) O(α(n)) O(α(n)) O ( α ( n ) ) O(α(n)) O(α(n))
Union O ( n ) O(n) O(n) O ( α ( n ) ) O(α(n)) O(α(n)) O ( α ( n ) ) O(α(n)) O(α(n))

其中, α ( n ) α(n) α(n) 是 反阿克曼函数(Inverse Ackermann Function) ,在大多数实际应用中接近于 O ( 1 ) O(1) O(1)。

可以认为: α ( n ) α(n) α(n) 是 "阿克曼函数需要递归多少次才能超过 n"。由于阿克曼函数增长极快, α ( n ) α(n) α(n) 几乎不会超过 5(在现实计算中)

5. 总结

并查集 是一种高效管理 不相交集合 的数据结构。

  • 路径压缩 + 按秩合并 可以使其接近 O ( 1 ) O(1) O(1) 时间。
  • 典型应用:检测环、最小生成树、动态连通性问题。
相关推荐
CSharp精选营18 小时前
关系型 vs 非关系型:从原理到选型,一文搞定数据库核心分类
数据结构·nosql·关系型数据库·非关系型数据库·技术选型
刘马想放假4 天前
Modbus 全栈技术解析:TCP、RTU、ASCII、RTU over TCP
数据结构·网络协议
北域码匠5 天前
冒泡排序太慢?鸡尾酒排序双向优化,原生 C# 零第三方库完整代码
数据结构·排序算法·泛型·c# 算法·鸡尾酒排序·原生 c# 开发·冒泡排序优化·嵌入式算法
Darling噜啦啦12 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
小小工匠13 天前
Redis - 事务机制:能实现 ACID 属性吗
数据结构·redis·性能优化·并发·持久化
玖玥拾13 天前
C/C++ 数据结构(七)栈、容器适配器
c语言·数据结构·c++··容器适配器
Qres82113 天前
算法复键——树状数组
数据结构·算法
牛油果子哥q13 天前
并查集(DSU)超精讲,路径压缩、按秩合并、万能模板、连通性判定、最小生成树与刷题实战全解
数据结构·c++·最小生成树·并查集
凌波粒13 天前
LeetCode--491.递增子序列(回溯算法)
数据结构·算法·leetcode
WL学习笔记13 天前
单项不带头不循环链表
数据结构·链表