对并查集的理解

并查集是一个维护不同集合间关系的数据结构,其中根节点是重点;

下面介绍一种并查集的优化手段:路径压缩;

先看代码:

	int find(int x) {
		if (p[x] != x) {
			p[x] = find(p[x]);
		}
		return p[x];
	}
//p[x]是存储一个点的父节点;
//当一个点为根节点时其父节点为自己,所以当搜索到一个点的,且判断这个根节点的父节点是不是自己就可以判断这个点是不是根节点,如果是根节点,那么我们就开始回溯沿着之前的路径开始返回,并将路过的点指向根节点
//这里来个形象的比喻,唐僧取经就可以看成找父节点的过程,路过一条路就要看当地国家的路牌,看是不是上面写着女儿国几个大字【女儿国几个字就好比判断一个点是不是根节点的条件】,如果不是女儿国而是雷音寺那么唐僧就沿着当地的人指着的方向走【父节点】,直到走到女儿国开始取经,取经完毕后唐僧一行人就开始原路返回普度众生,并命令让沿途的所有国家的人都指向女儿国,那么下次来女儿国取经时就可以直接到达,不用走岔路了;

这个其实就是并查集的核心代码,如果要将集合A和集合B结合,那么就找到a和b【分别为A和B中的元素】

p[find(a)]=find(b);

让a的根节点的父节点指向b节点的根节点,就可以实现AB两个集合合并;

如果要判断两个元素是否在一个集合中,那么就只需要

if( find(a)==find(b) )
cout<<"true";
else
cout<<"false";

这些是并查集的基本操作;

----------------------------------------------------------------------------------------------------------------------------

但是一般题中需要维护额外信息,比如一个集合中有多少个元素、一个点到根节点的距离是多少等等;

第一种:维护一个集合中的元素个数;

我们可以给每个点设置一个额外的变量,用来存储一个该集合中点的数量

struct point{
    int father,how_many;
}points[N];

其中how_many用来存储点数,当然这个只有根节点的how_many才有实际意义;

当我们合并两个集合时,那么直接让新的根节点的how_many等于两个集合的数量相加就行;

if( find( points[i].father )!=find( points[j].father) ){//防止两个点的集合相同
      points[ find(points[i].father) ].how_many+= points[ find(points[j].father) ].how_many
      points[ find( points[j].father ) ].father= points[ find(points[i].father) ].father
}

额....虽然长了一点,但意思很简单,就是先判断两个点的集合是否相同,如果不同,那么先让两个根节点的how_many变量相加,然后将两个集合合并,合并的代码已经写了,在上面;

第二种:点到集合的距离

这一点给本菜鸟的感觉有点像前缀和,这个维护点到集合距离可以分为两种情况一种是添加一个点然后算新点的距离,另外一种是删除一个点算受影响点的距离,说实话,这真和前缀和算前缀数组很像;

说这两种时不得不提在不改变集合的情况下计算一个点到根节点的距离;

//这里为了代码短一些,我就分别用两个数组,一个是记录到根节点的距离,一个是记录父节点
int father[N],len[N];
int find(int x){
     if(father[x]!=x){//再强调一遍,根节点的父节点是自己,这也是根节点才会有的特性
        int p=father[x];//这里先要存一下当前的父节点是什么,以便于下面累加距离
        father[x]=find( father[x] )
        len[x]+=len[p];
     }
     else
         return father[x];
}

有了这个基础,那么我们就可以开始计算删除一个点时怎么算距离了;

这边本菜鸟用的是找“树尖尖”的方法,所谓树尖尖就是树根的另一端,我们要删除一个点的方法就是让该点的子节点和父节点相连,注意,这里我已经将根节点和父节点的概念分开了,根节点是指该点的根节点是什么,父节点是上一个点是什么,之前因为父节点可以看作根节点,所有本菜鸟就没有区分,但是现在我们要将两个信息分别用数组存起来

int fa[N],son[N],root[N],len_fa[N],len_root[N];//len_fa是存到父节点的距离,len_root是存到根节点的距离;

void cut(x){
  fa[ son[x] ]=fa[x];//x点的儿子节点的父节点变为x的父节点;
  cut_len(x,len_fa[x]);//更新距离;
}

int cut_len(int x,int div){
        len_root[x]-=div;
        for(int i=0;son[i]!=0;i++){
            if(son[x][i]!=x)//树尖尖的特征就是儿子节点就是自己;
                cut_len(son[x],div);
            else
                return 0;
        }
}

int main(){
    int x; cin>>x;

    cut(x);//删除编号为x的节点
}

怎么理解呢,哪差分数组来理解,当我们将差分数组中的b[i]-=m;时,是不是在总的数组中a[x]-=m;

i<=x<=a.size();而0<=y<i,a[y]不受影响,我们干的就是手动将a[x]-=m;

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值