并查集的作用:
可以在近乎O(1)的时间内完成以下两个操作
1、将两个集合合并
2、询问两个元素是否在一个集合中
基本原理:
用“树”的形式来维护每一个集合,树根的编号就是整个集合的编号,每个结点存储它的父结点(如:p[x]表示x的父结点)
问题1:如何判断树根? A:if(p[x]==x),当前x就是树根了,因为除了树根其他的结点存储的都是它的父结点。
问题2:如何求x的集合编号? A:while(p[x]!=x) x=p[x];
问题3:如何合并两个集合? A:假设px是x的集合编号,py是y的集合编号,则p[px]=py,即将x集合的树根连接到y集合的树根下作为其子结点。
当前缺点及其优化?
缺点:这样子第二个操作,即求x的集合编号还是太慢了,需要while循环树的高度,需要优化。
优化:路径压缩!在第一遍求x的根结点时,一旦找到其根结点,将其路径上的所有结点的父结点都变成这个根结点,即树的高度变小了。
左图为路径压缩的简单图示,初始时树用黑色连线连接,从叶子结点找到根结点后,将路径上的所有非根结点的父结点都变成根结点,用红色连线表示。
代码实现:
题目参考acwing模板题:
代码参考:
#include<iostream> using namespace std; const int N=100010; int p[N]; int find(int x){ if(p[x]!=x)p[x]=find(p[x]);//巧妙的find函数核心,递归的同时实现了路径压缩,递归第一层找父结点,第二层找父结点......一直递归下去,找到根结点之后会原路返回,将所有路径上的结点的父结点都变成根结点 //递归需要加以限制,不进行限制的递归就会爆栈 return p[x]; //如果是下面这种写法,则没有进行状态压缩,只找了根结点,会超时 // while(p[x]!=x)x=p[x]; // return x; } int main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); int n,m; cin>>n>>m; for(int i=1;i<=n;i++){ p[i]=i; } while(m--){ char op; cin>>op; int a,b; cin>>a>>b; if(op=='M'){ p[find(a)]=find(b); } else{ if(find(a)==find(b))cout<<"Yes"<<endl; else cout<<"No"<<endl; } } return 0; }
标签:结点,递归,int,查集,树根,集合,数据结构,find From: https://www.cnblogs.com/Pworldlet/p/17359195.html