当前位置: 首页 > news >正文

【数据结构】并查集:从入门到精通

目录

  • 并查集的基本概念
  • 查找操作(Find)
    • 基本查找
    • 路径压缩优化
  • 合并操作(Union)
    • 基本合并
    • 按秩合并
      • 按高度合并
      • 按节点数量合并
    • 总结对比
  • 例题训练
    • P3367 【模板】并查集
  • 总结

引言:
在编程竞赛中,我们常常需要处理元素分组与集合操作的问题。并查集(Union-Find)作为一种高效的数据结构,能够高效便捷地实现集合的“合并”与“查找”操作,本篇文章将详细介绍并查集的原理和用途。

并查集的基本概念

并查集是一种树形的数据结构,主要支持**合并(Union)查找(Find)**两种操作:

  • 合并:将两个不相交的子集合合并成一个统一的集合(合并相应的根节点)。
  • 查找:查询元素所属的集合(找根节点)。

父节点数组 fa[]

  • 作用fa[x] 存储节点 x 的父节点。
  • 初始化:每个节点初始时是独立集合,指向自身。
for (int i = 1; i <= n; i++)fa[i] = i

例如,初始状态下 fa[1] = 1fa[2] = 2 等,每个节点自成一个集合。

查找操作(Find)

基本查找

查找的目标是找到元素所在集合的根节点,过程如下:

int Find(int x) {if (fa[x] == x) return x;  // 父节点是自身,找到根return Find(fa[x]);        // 递归查找父节点的根
}

路径压缩优化

为了加速后续查找,可在查找时压缩路径,让路径上的节点直接指向根:

int Find(int x) {if (fa[x] == x) return x;  return fa[x] = Find(fa[x]);  // 递归查找并直接指向根
}

合并操作(Union)

基本合并

将两个集合合并,做法是把一个集合的根指向另一个集合的根:

void Union(int x, int y) {fa[Find(x)] = Find(y);  // x 所在集合的根指向 y 所在集合的根
}

按秩合并

为避免合并后树过高影响效率,可按集合大小(或深度)合并,把小集合的根指向大集合的根。

按高度合并

1. 核心思想

合并时,让较矮树的根指向较高树的根
若两棵树高度相同,合并后新树高度会 +1 。
这样能保证树的高度尽可能小,让 find 操作(查找根)更快。

2. 关键变量

  • h[] 数组:h[root] 存储以 root 为根的树的高度
  • fa[] 数组:存储节点的父节点。

3. 代码展示

void Union(int x, int y){int rootx = Find(x);  // 找到 x 所在集合的根int rooty = Find(y);  // 找到 y 所在集合的根if(rootx != rooty)   // 不在同一集合才合并{   if(h[rootx] < h[rooty]) //x 所在树更矮 → 让 x 的根指向 y 的根 fa[rootx] = rooty;  else if(h[rootx] > h[rooty])// y 所在树更矮 → 让 y 的根指向 x 的根fa[rooty] = rootx;  else// 两棵树高度相同 → 随便选一个{fa[rootx] = rooty;  h[rooty]++;  // 合并后,新树高度 +1 }}
}

合并后树的高度被严格控制,find 操作的时间会更稳定,接近 O(α(n))O(α(n))O(α(n))(阿克曼函数反函数,极高效 )。

按节点数量合并

1. 核心思想

合并时,让较小集合的根指向较大集合的根。这样能保证大集合的树尽可能“粗壮”,小集合合并进来不显著增加树的高度,间接让 find 更快。

2. 关键变量

  • s[] 数组:s[root] 存储以 root 为根的集合的节点总数(初始时每个节点自身为集合,s[i]=1 )。
  • fa[] 数组:存储节点父节点。

3. 代码展示

void Union(int x, int y){int rootx = Find(x);  // 找 x 的根int rooty = Find(y);  // 找 y 的根if(rootx != rooty)// 不同集合才合并{   if(s[rootx] <= s[rooty])// x 集合更小(或相等)→ 让 x 的根指向 y 的根{  p[rootx] = rooty;  s[rooty] += s[rootx];  // y 集合大小 += x 集合大小 }else// y 集合更小 → 让 y 的根指向 x 的根{p[rooty] = rootx;  s[rootx] += s[rooty];  // x 集合大小 += y 集合大小 }}
}

总结对比

对比项按高度合并按节点数量合并
核心优化点控制树的高度,避免合并后树过高控制集合大小,让小集合合并进大集合
关键数组h[] 存树高,fa[] 存父节点s[] 存集合大小,fa[] 存父节点
适用场景对“树高度敏感”,或需严格限制树高需频繁查“集合大小”,或追求“通用高效”
实际效率接近 O(α(n))O(α(n))O(α(n)) ,树高控制更直接接近 O(α(n))O(α(n))O(α(n)) ,额外支持“集合大小查询”

例题训练

来一个模板题练练手:

P3367 【模板】并查集

题目来源

AC代码

#include<bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define int long long
#define PII pair<int,int>
#define fi first
#define se second
#define endl '\n'
const int N=2e5+6;
int n,m,f[N],z,x,y,h[N];
int Find(int x)
{if(f[x]==x)return x;else return Find(f[x]);
}
void Union(int x,int y)
{int rx=Find(x);int ry=Find(y);if(rx!=ry){if(h[rx]<h[ry])f[rx]=ry;else if(h[rx]>h[ry])f[ry]=rx;else{f[rx]=ry;h[ry]++;}}
} 
void solve()
{cin>>n>>m;for(int i=1;i<=n;i++)f[i]=i,h[i]=1;while(m--){cin>>z>>x>>y;if(z==1)Union(x,y);else if(z==2){int tx=Find(x);int ty=Find(y);if(tx==ty)cout<<"Y"<<endl;elsecout<<"N"<<endl;}}
}
signed main()
{IOS;int _=1;
//	cin>>_;while(_--)solve();return 0;
}

总结

并查集通过 find(含路径压缩)和 union(含按秩合并)操作,高效处理动态集合的合并与查找,时间复杂度近似 O(α(n))O(α(n))O(α(n))(阿克曼函数反函数,极接近常数 ),广泛应用于连通性问题。掌握其原理与优化,能轻松应对各类集合操作问题 。

http://www.dtcms.com/a/328443.html

相关文章:

  • 《Linux基础知识-1》
  • docker-compose搭建 redis 集群
  • 阿里巴巴开源多模态大模型-Qwen-VL系列论文精读(一)
  • VBS 时间函数
  • 基于 libwebsockets 库实现的 WebSocket 服务器类
  • Shader warning in ‘Universal Render Pipeline/Particles/Simple Lit‘
  • provide()函数和inject()函数
  • 【UEFI系列】Super IO
  • VUE+SPRINGBOOT从0-1打造前后端-前后台系统-语音评测
  • 嵌入式学习(day25)文件IO:open read/write close
  • VGG改进(2):基于Local Attention的模型优化
  • 书籍数组中未出现的最小正整数(8)0812
  • 《飞算JavaAI:新一代智能编码引擎,革新Java研发范式》
  • 跑腿平台开发实战:同城O2O系统源码的模块化与可扩展性方案
  • 每日一练:将一个数字表示成幂的和的方案数;动态规划、深度优先搜索
  • 【Altium designer】快速建立原理图工程的步骤
  • 2025开放计算技术大会|开源开放推动系统创新 加速AIDC全球协作
  • 过拟合、欠拟合与方差/偏差的关系
  • Langchain结合deepseek:框架+模型的AI测试实践
  • 小白学习pid环控制-实现篇
  • 杰里平台7083G 如何支持4M flash
  • 【oracle闪回查询】记录字段短时间被修改的记录
  • MyBatis-Plus核心内容
  • AAC音频编码器技术详解:原理、应用与发展
  • Java数组排序
  • 嵌入式系统分层开发:架构模式与工程实践(四)(状态机的应用和面向对象的编程)
  • redis认识缓存击穿
  • 特征工程--机器学习
  • [ 数据结构 ] 时间和空间复杂度
  • Linux中Apache与Web之虚拟主机配置指南