【数据结构】——并查集

概念所谓并查集,就是把一个集合合并再进行查询。


并查集基本操作:

1、将a、b两个元素合并在一个集合

2、查询a、b是否在一个集合


那么,我们这是就要思考:如何将每个元素合并在一个集合呢?它们之间是否存在着某个标志?

很显然,标志是肯定要存在的。

首先,我们可以将几个元素组成的集合看成一棵树,每个元素就是一个节点。那么我们在这里就需要借助一下树的特性:每个点都有祖先节点。既然如此,我们便可以把标志转移到各个元素的祖先节点上:

fa[x]表示元素x的父亲节点,若想找其祖先节点,只需要一直递归下去便可

那么首先就把每个元素的父亲节点定为自己

那么并查集有以下几段优美的代码:

查找公共祖先
int getfa(int k){
    if(k==fa[k]) return k;
    fa[k]=getfa(fa[k]);
    return fa[k];
}


合并
x=getfa(x),y=getfa(y);
if (x!=y) fa[y]=x;

判断是否在同一个集合
bool panduan(int a,int b){
    return(getfa(a)==getfa(b));
}

//好吧也许我写得不够优美


那么上几道例题:


一、亲戚

  题目描述:有n个人,m个关系,k个查询。2到k行,a、b,说明a、b为亲戚(间接关系也算);k+1到最后,x、y,查询它们是否为   亲戚关系

  题意分析:并查集的一道模板题。将a、b合并在一个集合,就无需考虑间接关系;那么查询时只需要判断x、y是否在同一集合便可

  那么代码如下:

#include<bits/stdc++.h>
using namespace std;
int fa[1000001]={},n,m,p;
int getfa(int k){
    if(k==fa[k]) return k;
    fa[k]=getfa(fa[k]);
    return fa[k];
}//查询祖先节点
int main(){
    cin>>n>>m>>p;
    for (int i=1;i<=n;i++) fa[i]=i;
    for (int i=1;i<=m;i++){
	    int x,y;
	    cin>>x>>y;
	    x=getfa(x),y=getfa(y);
	    if (x!=y) fa[y]=x;//如果不在同一集合,便合并,使他们成为亲戚
	}
	for (int i=1;i<=p;i++){
	    int x,y;
	    cin>>x>>y;
	    int xx=getfa(x),yy=getfa(y);
	    if (xx==yy)cout<<"Yes"<<endl;//如果在同一个集合,说明是亲戚
	    else cout<<"No"<<endl;//不然择不是
	}
	return 0;
}


二、犯罪团伙

   题意描述:有a、b两个人,他们要么是朋友,要么是敌人。 而且有一点是肯定的: a的朋友的朋友是a的朋友; a的敌人的敌人也是a的朋友。 两个强盗是同一伙的当且仅当他们是朋友。现在小明同学想知道有多少个犯罪同伙

  题目分析:这道题我开始做的时候也是毫无思路,但仔细读了几遍题目后,就知道了一个重点——敌人的敌人就是朋友!那么这样我们就可以用一个数组f[x]存储x的敌人。如此,我们就可以把敌人的敌人合并在一个集合中,即是朋友,在一个团伙

  合并完后,我们便可以查询有几个团伙

  那么代码如下:

#include<bits/stdc++.h>
using namespace std;
int a[1000001]={},fa[1000001]={},n,m;
int getfa(int k){
    if (k==fa[k]) return k;
    fa[k]=getfa(fa[k]);
    return fa[k];
}
int main(){
    cin>>n>>m;
    for (int i=1;i<=n;i++) fa[i]=i;
    for (int i=1;i<=m;i++){
	    int x,y;
	    char ch;
	    cin>>ch;
		cin>>x>>y;
		if (ch=='F'){
		    int xx,yy;
		    xx=getfa(x);yy=getfa(y);
		    if (xx!=yy) fa[yy]=xx;//如果是朋友,直接合并
		}
		else{
			int xx,yy;
		    if (a[x]==0)a[x]=y;
		    if (a[y]==0)a[y]=x;//敌人的敌人是朋友,记录敌人的敌人
		    xx=getfa(x);yy=getfa(a[y]);//把自己的祖先和敌人的敌人的祖先找到
		    if (xx!=yy) fa[yy]=xx;//合并
		    xx=getfa(y);yy=getfa(a[x]);//同样
		    if (xx!=yy) fa[yy]=xx;//合并
		}
	}
	bool f[100001]={};
    int s=0;
	for (int i=1;i<=n;i++){
	    int x=getfa(i);
	    if (!f[x]) s++,f[x]=1;//如果祖先没出现过,说明是新的一个集合,就加一
	}
	cout<<s;//输出
	return 0;
}

猜你喜欢

转载自blog.csdn.net/huang_ke_hai/article/details/80447516
今日推荐