并查集(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) 时间。
  • 典型应用:检测环、最小生成树、动态连通性问题。
相关推荐
雾里看山3 小时前
顺序表VS单链表VS带头双向循环链表
数据结构·链表
好好研究6 小时前
学习栈和队列的插入和删除操作
数据结构·学习
挺菜的9 小时前
【算法刷题记录(简单题)003】统计大写字母个数(java代码实现)
java·数据结构·算法
2401_858286119 小时前
125.【C语言】数据结构之归并排序递归解法
c语言·开发语言·数据结构·算法·排序算法·归并排序
双叶83610 小时前
(C++)学生管理系统(正式版)(map数组的应用)(string应用)(引用)(文件储存的应用)(C++教学)(C++项目)
c语言·开发语言·数据结构·c++
学不动CV了13 小时前
数据结构---链表结构体、指针深入理解(三)
c语言·arm开发·数据结构·stm32·单片机·链表
算法_小学生15 小时前
LeetCode 287. 寻找重复数(不修改数组 + O(1) 空间)
数据结构·算法·leetcode
Wo3Shi4七18 小时前
哈希冲突
数据结构·算法·go
V我五十买鸡腿19 小时前
顺序栈和链式栈
c语言·数据结构·笔记·算法
七灵微20 小时前
数据结构实验习题
数据结构