刚开始接触就对这迷迷糊糊的,一直搞不懂,后来发现用处还不少,不得不从头来过啊!
先来个高级定义:并查集是一种高级的数据结构,能用于处理不相交集合的合并及查询。求最小生成树、亲戚关系的判定、最小公共祖先等都要用到。
先来个高级定义:并查集是一种高级的数据结构,能用于处理不相交集合的合并及查询。求最小生成树、亲戚关系的判定、最小公共祖先等都要用到。
并查集的使用主要有三步:定义数组并初始化、查找元素的根节点、合并不相交的集合。
1、定义数组并初始化:
int per[1001],n;//根据具体情况定义
for(int i=1;i<=n;i++){//i从1开始
per[i]=i;//初始化为自身
}
2、接下来是查找元素的根节点,判断有几棵树:
int findx(int x){
int r=x;//局部变量
while(per[r] !=r)//根节点满足的条件为per[r]==r
r=per[r];//(*)
return r;//返回根节点
}
(*)那条语句一直很懵逼,当初一直在想为什么不直接用个if语句,多省事!后来想了想,还是用循环比较靠谱,因为当r=pre[r]时,pre[r]=pre[pre[r]]
,慢慢体会吧。。。
3、下一步就是树与树之间的合并了:
void merge(int x,int y){
int fx=find(x);
int fy=find(y);
if(fx!=fy)//如果x,y的根节点不相等
pre[fx]=fy;//x,y中以y为根节点,也可写成pre[fy]=fx,以x为根节点;
}
好不容易理解了一点点儿,又出来一个路径压缩。。。
官方的说法:在使用并查集查找时,如果查找次数很多,那么上述的2查找方法可能就会超时,所以需要代码优化,即路径压缩(就是在每次查找时,令查找路径上的每个节点都直接指向根节点,技术有限,图就不配了,问度娘)。方法有两种,细细道来。。
(1)、递归:
int find(int x){
if (x != parent[x]){
parent[x] = find(parent[x]);//回溯时的压缩路径
}//从x结点搜索到祖先结点所经过的结点都指向该祖先结点
return parent[x];
}
但是采用递归的方式压缩路径时,有可能会造成栈溢出(其实我也不知道是为啥),下面说方法二:
int find(int x){
int k,j,r;
r=x;
while(r!=per[r])//查找根节点
r=per[r];//找到根节点,用r记录下
k=x;
while(k!=r){//非递归路径压缩操作
j=per[k];//用j暂存per[k]的父节点
per[k]=r;//per[x]指向根节点
k=j;//k移到父节点
}
return r;//返回根节点的值