并查集的常见用法
作用
- 将两个集合合并
- 询问两个元素是否在一个集合当中
基本原理
每个集合用一颗树来表示。树根的编号就是整个集合的编号。每个节点储存他的父亲节点,p[x]表示x的父节点
p[x]就表示x属于哪个集合。
问题
- 如何判断树根(祖宗集合) :if ( p[x] == x );
- 如何求x的集合编号 : while ( p[x] != x ) x = p[x];
- 如何合并两个集合 :p[x] 是 x 的集合编号,p[y] 是 y 的集合编号 ,让p[x] = y;
最最最重要
递归的含义就相当于你问你爸爸你的祖先是谁,你爸爸也不知道,爸爸就去问爷爷,然后你的爷爷也不知道,爷爷就去问你的太爷爷,你的太爷爷年纪太大了,啥也不记得,就去问你的祖先
这个时候注意,你的祖先是知道自己是谁的,所以x ==p[x]
p[x]中储存的是每个节点的父节点,一开始并不是树状的,而是一个个单独的节点,每个节点都是根节点,都有集合编号,然后通过不断的合并才形成的树状结构
这个函数是并查集中最重要的部分,背过理解
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]); //路径压缩
return p[x];
}
模板
(1)朴素并查集:
朴素并查集:
int p[N]; //存储每个点的祖宗节点
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ ) p[i] = i;
// 合并a和b所在的两个集合:
p[find(a)] = find(b);
(2)维护size的并查集:
size只有祖节点的有意义
要特别注意所有处理size的地方,都要“归根结底”
维护size的并查集:
int p[N], size[N];
//p[]存储每个点的祖宗节点, size[]只有祖宗节点的有意义,表示祖宗节点所在集合中的点的数量
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ )
{
p[i] = i;
size[i] = 1;
}
// 合并a和b所在的两个集合:
size[find(b)] += size[find(a)];
p[find(a)] = find(b);
(3)维护到祖宗节点距离的并查集:
维护到祖宗节点距离的并查集:
int p[N], d[N];
//p[]存储每个点的祖宗节点, d[x]存储x到p[x]的距离
// 返回x的祖宗节点
int find(int x)
{
if (p[x] != x)
{
int u = find(p[x]);
d[x] += d[p[x]];
p[x] = u;
}
return p[x];
}
// 初始化,假定节点编号是1~n
for (int i = 1; i <= n; i ++ )
{
p[i] = i;
d[i] = 0;
}
// 合并a和b所在的两个集合:
p[find(a)] = find(b);
d[find(a)] = distance; // 根据具体问题,初始化find(a)的偏移量
合并集合
题目描述:
p[x]就表示x属于哪个集合。
code:
int find(int x)
{
if(p[x]!=x) // 去找祖宗
{
int t = find(p[x]); // 找到祖宗
p[x] = t; // 祖宗秒变爹 路径压缩
}
return p[x]; //返回的是节点的祖宗
}
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++) p[i]=i;
while (m -- )
{
char c;
int a,b;
cin>>c>>a>>b;
if(c=='M')
{
p[find(a)]=find(b); //让a的祖宗都是b的,也就是说,让a的整个集合都属于b;
}
else
{
if(find(a)==find(b)) puts("Yes");
else puts("No");
}
}
return 0;
}
null
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理