并查集是什么
并查集是一种用来管理元素分组情况的数据结构。并查集可以高效地进行如下操作。不过需要注意并查集虽然可以进行合并操作,但是却无法进行分割操作。
- 查询元素a和元素b是否属于同组。
- 合并元素a和元素b所在的组。
并查集的简单案例
n个城市中(编号1~n),有一些城市间会有无向的通路。
现在小明想从a城市到b城市,判断是否能实现?即a和b是否有直接或间接的道路连通起来。
如有4个城市,其中1和2有道路,3和4有道路。
那么1 2可以相通,3 4可以相通,其他情况均不行。
简单的思考可以发现,如果我们把有通路的记为同一堆,只需判断a和b是否在同一堆里即可。
所以我们现在要实现的就是维护他们之间的连通关系。
为了查询的方便,我们用一堆里面的某一个城市当作这个堆的代表(老大),我们判断a和b即判断他们的老大是否相同。这个老大选择谁其实是无所谓的,我们一般用这个堆里面的第一个元素就当这个堆的老大。
现在我们来看看到底如果操作
并查集的应用
主要说明元素的查询和合并
- 查询
顾名思义,这个操作就是查询某一个元素的老大是谁
查询之前我们需要把每一个pre[x]初始化,表示它自己就是他所在堆的老大
for(int i=1;i<=n;i++)
pre[i]=i;
我们用pre[x]表示x的上级(不一定是老大)一直向上找,直到pre[x]=x就可以知道x就是x所在堆的老大
int findroot(int x)
{
if(pre[x]==x)
return x;
else
return findroot(pre[x]);
}
但这样有一个弊端,就是同一个x会被查询很多次,所以我们可以用一个小技巧,用pre[x]来记录它尽量上级的一个上级
int findroot(int x)
{
if(pre[x]==x)
return x;
else
return pre[x]=findroot(pre[x]);
}
这样我们就维护可以查询到x的老大是谁,如果有一条路把x和y连起来了,那么我们就需要把x和y所在的两个堆合并起来。
- 合并
如果我们需要合并a和b的对应的两个堆,我们只需要将其中一个堆的老大root2的上级改为另一个堆的老大root1,这样他们两个堆就通过root2联系起来了
其中的x和y分别是两个堆的root
void mix(int x,int y)
{
pre[y]=x;
}
练习题目
/*
* @Description:
* @Autor: Kadia
* @Date: 2020-06-01 18:23:52
* @LastEditors: Kadia
* @connect: vx:ccz1354 qq:544692713
* @LastEditTime: 2020-06-01 20:01:29
* nk
*/
#include <bits/stdc++.h>
using namespace std;
int pre[100005];
int findroot(int x)
{
if(pre[x]==x)
return x;
else
return pre[x]=findroot(pre[x]);
}
void mix(int x,int y)
{
pre[y]=x;
}
int main()
{
int n,m;
cin >> n >> m ;
for(int i=1;i<=n;i++)
pre[i]=i;
int x,y;
int all=n;
for(int i=1;i<=m;i++)
{
cin >> x >> y;
int root1=findroot(x);
int root2=findroot(y);
if(root1!=root2)
{
mix(root1,root2);
all--;
}
}
cout << --all << endl ;
return 0;
}