并查集
操作一:初始化
用一个数组fa【】来储存每个元素的父节点,一开始将所有的元素的父节点设置为自己。
void 初始化(int n)
{
for(int i=1;i<=n;i++)
fa[i]=i;
}
操作二:查询
找父节点的父节点的......也就是祖先。
int find(int i)
{
if(fa[i]==i)//找到了祖先直接返回
return i;
else{//没有找到就继续找
return find(fa[i]);
}
}
但此时会发现如果父子连很长这时找到最下面的祖先就需要寻找很多次,就需要优化吧路径缩短:
int find(int i)
{
if(fa[i]==i)
{
return i;
}
else{
fa[i]=find(fa[i]);//插入这一步缩短路径
return fa[i];
}
}
操作三:合并
void unionn(int a1,int b1)
{
int i,j;
i=find(a1);
j=find(b1);//找到两个元素的祖先
fa[i]=j;//将i的祖先指向j的祖先,这样他们两就是一家人了
}
练习题:
P3367 【模板】并查集
P3367 【模板】并查集 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll a[10010];
void unionn(int a,int b);
int find(int i);
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
a[i]=i;
while(m--)
{
int z,x,y;
cin>>z>>x>>y;
if(z==1){
unionn(x,y);
}
else{
if(find(x)==find(y))cout<<'Y'<<endl;//有共同祖先说明已经被合并
else cout<<'N'<<endl;
}
}
}
void unionn(int a1,int b1)
{
int i,j;
i=find(a1);
j=find(b1);
a[i]=j;
}
int find(int i)
{
if(a[i]==i)
{
return i;
}
else{
a[i]=find(a[i]);
return a[i];
}
}
P1111 修复公路
P1111 修复公路 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
主要思路:连接n个点最小路段只需要n-1条,先将所有路所需要的时间按从小到大排序,优先选取小时间的路段修路,修每条路时需要判断所修的路是否有环,有就是判断是否有共同的祖先,有环就不修。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node {
int x,y,t;
}a[100005];
int fa[100005];
void unionn(int a,int b);
int find(int i);
int compare(node a1,node b1);
int main()
{
int n,m,i;
cin>>n>>m;
for(i=1;i<=n;i++)fa[i]=i;//初始化
for(i=1;i<=m;i++)cin>>a[i].x>>a[i].y>>a[i].t;
sort(a+1,a+m+1,compare);//排序
int ans=a[1].t;
for(i=1;i<=m;i++)
{
if(find(a[i].x)!=find(a[i].y))//判断是否有共同祖先,有闭环就会多余
{
unionn(a[i].x,a[i].y);//合并,通路
ans=max(ans,a[i].t);//获取路的最大时间
n--;
if(n==1)cout<<ans<<endl;
}
}
if(n!=1)cout<<-1;//不行就输出-1
}
void unionn(int a1,int b1)
{
int i,j;
i=find(a1);
j=find(b1);
fa[i]=j;
}
int find(int i)
{
if(fa[i]==i)
{
return i;
}
else{
fa[i]=find(fa[i]);
return fa[i];
}
}
int compare(node a1,node b1)
{
return a1.t<b1.t;
}