并查集

并查集作用:将众多元素按条件构造成一个集合,举个不好的例子,1,2,3,4,5都是数字而abc是字母,所以把他们分成2个集合,比较好理解的例子是百度上那个:

若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。 规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚

 

并查集的基础操作有:

①将每个元素都当成一个集合,此时每个元素的根节点都是自身

②查找每个集合的根节点【代码里的find()】

③合并相同根节点的集合

以下从图的角度理解,图中每个字母代表一个元素,箭头指向的为根节点【引用CYJB博客园图片】

初始化,每个元素(节点)指向自己:

查找:查找过程能起到路径压缩作用,见图,如果不压缩,那么要找d的根节点需要先找到c,再找到c的父节点...,路径压缩使多个节点直接指向根节点,而不需经过父节点(即父节点就是根节点)

合并:将两个根节点不同的集合合并,即让其中一个集合成为另一个集合原先根节点的新根节点

//以上图片来自网上博客

例题:杭电1213 How Many Tables

题意:某人要办生日,来的朋友不想和不认识的人坐同一张桌子,给定朋友数和朋友的认识关系,问至少需要多少桌子(A认识B,B认识C,那么ABC就是相互认识了)

#include<cstdio>
#define MAX 1002
int f[MAX];
int find(int x)
{
	if( x == f[x] ) return x;
	return f[x] = find(f[x]);
}

void merge(int x,int y)
{
	x = find(x);
	y = find(y);
	if(x != y)
		f[x] = y; //将y的集合合并到x的集合
}

int main()
{
	int i,n,m,t,a,b,res;
//	freopen("a.txt","r",stdin);
	while( ~scanf("%d",&t) )
	{
		while(t--)
		{
			res = 0;
			scanf("%d%d",&n,&m);
			for(i=1;i<MAX;i++)		//初始化假定每个人只认识自己  
				f[i] = i;

			while(m--)
			{
				scanf("%d%d",&a,&b);	
				merge(a,b);		//输入ab,他们认识,合并在一同一集合  
			}
			for(i=1;i<=n;i++)
			{ 				//判断有多少个集合就是有多少群人相互认识,就有多少桌子 
				if( f[i] == i )		 
					res++;		//判断集合数目是根据根节点指向自己  
			}
			printf("%d\n",res);
		}
	}
	return 0;
}	

这种递归形式的路径压缩find()有个缺点:数据大(如10W)时可能栈溢出,于是有了非递归的路径压缩find()

int find(int x)
{
    if( x == f[x] ) return x;
    int rt=x;
    while(rt!=f[rt])      //找到根结点
        rt=f[rt];
    int fx;
    while(x!=rt)
    {
        fx=f[x];
        f[x]=rt;
        x=fx;
    }
	return x;
}


hiho 1066 无间道之并查集

也是裸题,输入的是字符串,需要map一下,注意数组大小是 2*n,n个输入有2*n个字符串

#include <iostream>
#include <algorithm>
#include <math.h>
#include <string.h>
#include <cmath>
#include <map>

using namespace std;
const int maxn = 100005;
const int maxM = 25300*502;

int root[maxn*2];
int n;
int findRoot(int cur){
    if(cur == root[cur]){
        return cur;
    }
    return root[cur] = findRoot(root[cur] );
}

int merge(int x,int y){
    int rootX = findRoot(x);
    int rootY = findRoot(y);
    if(rootX != rootY){
        root[rootX] = rootY;
    }
}

int main() {
    int op;
    string str1,str2;
    map<string,int>mp;
    while (cin >> n ) {
        for (int i = 0; i <= n; i++) {
            root[i] = i;
        }
        mp.clear();

        int cnt = 0;
        for (int i = 0; i < n; i++) {
            cin >> op >> str1 >> str2;
            
            int idx1 ,idx2 ;
            idx1 = mp[str1];idx2 = mp[str2];

            if( mp[str1] == 0){
                idx1 = mp[str1] = ++cnt;
            }
            if( mp[str2] == 0){
                idx2 = mp[str2] = ++cnt;
            }   
            if(op==1){
                if( findRoot(idx1) == findRoot(idx2) ){
                    cout<<"yes"<<endl;
                }else{
                    cout<<"no"<<endl;
                }
            }else{
                merge(idx1,idx2);
                // for (int i = 0; i < cnt; i++) {
                    // cout<<word[i]<< " " << i << " "<<findRoot(i) << endl;
                // }
            }
        }
    }
    return 0;
}


当我们希望集合不仅仅表示元素间是不是同属集合,还需要表示它们的关系时,就需要另开数组

POJ 2492 A Bug's Life

 

输入a,b表示ab异性,问是不是会造成矛盾,设r[]=0表示同性,r[]=1表示异性。

#include<stdio.h>
const int maxn=2015;
int f[maxn],r[maxn];
void init(int n)
{
    for(int i=1;i<=n;i++)
    {
        f[i]=i;
        r[i]=0;
    }
}
int find(int x)
{
    if(x==f[x]) return x;

    int fx=f[x];
    f[x]=find(f[x]);
    r[x]=(r[fx]+r[x])%2;
    return f[x];
}
void merge(int x,int y)
{
    int fx=find(x);
    int fy=find(y);

    if(fx!=fy)
    {
        f[fy]=fx;
        r[fy]=(r[x]+r[y]+1)%2;
    }
}

#define sc(x) scanf("%d",&x);
int main()
{
//     freopen("1.in","r",stdin);
    int t;
    sc(t)
    for(int ce=1;ce<=t;ce++)
    {
        int n,m;
        sc(n);sc(m);
        init(n);
        int no=0;
        while(m--)
        {
            int x,y;
            sc(x)   sc(y)
            if(no)  continue;
            if(find(x)==find(y))
            {				//同一集合时(r[x]+r[y])%2为0就与输入的关系1矛盾
                if(r[x]==r[y])
                    no=1;
            }
            else		//不同集合的合并肯定不会有问题
                merge(x,y);
        }
        if(no)
            printf("Scenario #%d:\nSuspicious bugs found!\n\n",ce);
        else
            printf("Scenario #%d:\nNo suspicious bugs found!\n\n",ce);
    }
    return 0;
}

POJ 1182 食物链

#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
#define ll long long
#define eps 10^(-6)
#define Q_CIN ios::sync_with_stdio(false);
#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define FOR( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define CLR( a , x ) memset ( a , x , sizeof (a) );
#define RE freopen("1.in","r",stdin);
#define WE freopen("1.out","w",stdout);
#define MOD 10009
#define debug(x) cout<<#x<<":"<<(x)<<emdl;

const int maxn=50010;

int f[maxn],r[maxn];
//r=0:同类,r=1:被父亲吃,r=2:吃父亲
int find(int x)
{
    if(f[x]==x) return x;

    int fx=f[x];        //原父亲(fx)
    f[x]=find(f[x]);    //ffx
    r[x]=(r[x]+r[fx])%3;
    return f[x];
}
void init(int n)
{
    for(int i=1;i<=n;i++)
    {
        f[i]=i;
        r[i]=0;
    }
}
int merge(int x,int y,int d)
{
    int fx=find(x);
    int fy=find(y);
    f[fy]=fx;       //被 x 吃,所以以 x 的根为父
    r[fy]=(3-r[y] + d-1 + r[x])%3;
}
#define sc(x) scanf("%d",&x);

int main()
{
//    RE
    int n,k,x,y,t,d;
    sc(n) sc(k)
    {
        int cnt=0;
        init(n);
        while(k--)
        {
            sc(d) sc(x) sc(y)
            if(x>n||y>n)    cnt++;
            else if(d==2&&x==y)  cnt++;
            else if(find(x)==find(y))
            {
                if(d==1 && r[x]!=r[y])cnt++;    //不同类
                if(d==2 && (r[x]+1)%3!=r[y])  cnt++;
            }
            else
                merge(x,y,d);
        }
        printf("%d\n",cnt);
    }
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
并查集(Disjoint Set)是一种数据结构,用于解决集合的合并和查找问题。在Python中可以使用类来实现并查集。引用展示了一个简单的并查集类的代码实现,其中包括了初始化集合、查找集合、合并集合和判断两个元素是否在同一个集合中的方法。另外,引用和展示了对并查集代码的优化,包括路径压缩和按秩合并等技巧,以提高并查集的效率。 在Python中使用并查集可以解决一些实际问题,如求解岛屿个数、朋友圈等。通过将问题转化为集合的合并和查找操作,可以使用并查集来高效地解决这些问题。 所以,如果你需要在Python中实现并查集,可以参考以上的代码实现和优化方法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [python 数据结构与算法——并查集](https://blog.csdn.net/itnerd/article/details/103916115)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [并查集Python版](https://blog.csdn.net/XZ2585458279/article/details/127274576)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值