zoukankan      html  css  js  c++  java
  • 并查集

    何谓并查集

    并查集实际上就是并集和查集的过程。那么什么是集呢?你可以把他近似地理解为一棵树。即一个根结点连着无数个子节点。

    并查集的实现

    给出例题:例题源网站(洛谷)
    这里附:
    题目描述

    如题,现在有一个并查集,你需要完成合并和查询操作。

    输入输出格式

    输入格式:
    第一行包含两个整数N、M,表示共有N个元素和M个操作。

    接下来M行,每行包含三个整数Zi、Xi、Yi

    当Zi=1时,将Xi与Yi所在的集合合并

    当Zi=2时,输出Xi与Yi是否在同一集合内,是的话输出Y;否则话输出N

    输出格式:
    如上,对于每一个Zi=2的操作,都有一行输出,每行包含一个大写字母,为Y或者N

    输入输出样例

    输入样例#1:
    4 7
    2 1 2
    1 1 2
    2 1 2
    1 3 4
    2 1 4
    1 2 3
    2 1 4
    输出样例#1:
    N
    Y
    N
    Y
    这就是最最基础的并查集模版了

    初始化

    我们会用到一个father数组,记录每一个节点的根结点
    最初要把每一个节点都当作一个集,即

    	father[i]=i;
    

    这里我们需要用一个循环来实现,完整代码如下

    	for(int i=1;i<=n;/*n即n个元素*/i++)
    		father[i]=i;
    

    然后初始化就完成啦。。

    函数

    并查集一定会用到一个getfather函数(其实名字你随便起)
    这个函数就是去找根节点

    int getfather(int x)//找x的根结点
    {//这其实是一个递归函数
    	if(father[x]==x)//边界条件,如果x的根结点就是x(也就是说它没有更上边的祖宗了)
    		return x;//就会返回x的值
    	else
    	{
    		father[x]=getfather(father[x]); //不然的话就会一级一级找上去(先找到它爸爸,在找到它爸爸的爸爸,再找到它爸爸的爸爸的爸爸。。。。。)
    	}
    	return father[x];
    }
    

    读入

    先上代码吧。。毕竟没什么难度

    for(int i=1;i<=m;i++)
    	{
    		scanf("%d%d%d",&z[i],&x[i],&y[i]);
    		z1[i]=getfather(x[i]);//z1数组用于存储每一个x[i]的根结点
    		z2[i]=getfather(y[i]);//z2数组用于存储每一个y[i]的根结点
    	}
    

    并集

    目的就是让一个集合的根结点成为另一个集合的根结点,这样这两个集合就有同一个根结点,就起到了合并的作用。(比如说小明的爷爷也是小李的爷爷,他俩就是亲戚)
    代码如下

    if(z[i]==1)
    	{
    		if (z1[i]!=z2[i])
    			Union(z1[i],z2[i]);
    	}
    

    这个Union是我自己写的一个函数,它长这样:

    void Union(int xx,int yy)
    {
    	int f1=getfather(xx);//f1就是 xx的根结点
    	int f2=getfather(yy);//f2就是yy的根结点
    	father[f1]=f2;//让xx的根结点成为yy的根结点(上面有解释)
    	return;
    }
    

    由于这是一个void函数,所以你也可以直接把代码 粘出来。

    查集

    鉴于我们已经做过并集的工作,查集就变得轻松了很多,我们只需要确认两个点是否有 同一个根结点就行了,代码如下:

    if(z[i]==2)
    	{
    		if(getfather(x[i])==getfather(y[i]))//如果 x[i]的根结点就是y[i]的根结点
    		{
    			cout<<"Y"<<endl;//就输出y
    		}
    		else
    		{
    			cout<<"N"<<endl;//不然就输出 n
    		}
    	}
    

    应该不难懂吧?

    本题完整代码如下

    #include<iostream>
    #include<cstdio>
    using namespace std;
    int n,m,father[200000];
    int z[200000],y[200000],x[200000];
    int getfather(int x)
    {
    	if(father[x]==x)
    		return x;
    	else
    	{
    		father[x]=getfather(father[x]); 
    	}
    	return father[x];
    }
    void Union(int xx,int yy)
    {
    	int f1=getfather(xx);
    	int f2=getfather(yy);
    	father[f1]=f2;
    	return;
    }
    int main()
    {
    	int z1[200000],z2[200000];
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)
    	{
    		father[i]=i;
    	}
    	for(int i=1;i<=m;i++)
    	{
    		scanf("%d%d%d",&z[i],&x[i],&y[i]);
    		z1[i]=getfather(x[i]);
    		z2[i]=getfather(y[i]);
    	}
    	for(int i=1;i<=m;i++)
    	{
    		if(z[i]==1)
    		{
    			if (z1[i]!=z2[i])
    				Union(z1[i],z2[i]);
    		}
    		if(z[i]==2)
    		{
    			if(getfather(x[i])==getfather(y[i]))
    			{
    				cout<<"Y"<<endl;
    			}
    			else
    			{
    				cout<<"N"<<endl;
    			}
    		}
    	}
    	return 0;
    }
    

    还是建议自己去写一遍。
    这里还有几道题
    极其类似模版的题
    局域网
    营救
    不会的可以自己去看一下题解。

    tks

  • 相关阅读:
    git 从远程仓库指定分支克隆代码到本地
    vue路由懒加载
    ES6中拓展运算符 ...
    Mysql 安装-windows X64
    mysql-Federated存储方式,远程表,相当于sql server的linked server
    优化临时表使用,SQL语句性能提升100倍
    MySQL行锁深入研究
    mysql 队列 实现并发读
    mysql 常用sql
    mysql分表的3种方法
  • 原文地址:https://www.cnblogs.com/opbnbjs/p/9385693.html
Copyright © 2011-2022 走看看