0x41并查集

本文详细介绍了并查集这种数据结构,包括其基本操作如查找和合并,并讲解了路径压缩和按秩合并的优化策略。通过示例代码展示了如何实现并查集,以及在实际问题中的应用,如程序自动分析、超市商品管理和食物链问题的解决。
摘要由CSDN通过智能技术生成

并查集:一种可以动态维护若干个不重叠的集合,并支持合并与查询的数据结构。
1.get :查询元素属于哪一个集合
2.merge:合并两集合

使用“代表元法”表示并查集:选择固定元素代表集合

使用树形结构存储每个集合,树上每一个节点都是一个元素,树根是集合的代表元素。整个并查集是一个森林,通过维护数组fa[n]记录整个森林,fa[x]保存x 的父节点,特别的,树根的fa[x]值等于自身。
合并操作:将其中一个树根直接连接到另一棵树上,即fa[root1]=root2
查询归属:递归访问父节点直到树根。

路径压缩:(常用)
在每次查询过程中,将过程中访问过的节点(即所查询元素的全部祖先)直接指向树根:优化查询效率
按秩合并:
将集合的秩(可以是树的深度or集合大小等)记录树根上,合并时将秩较小的树根作为秩较大的树根的子节点。
(将小的结构合并到大的结构中去,只增加小的结构的查询代价)

(1)朴素并查集:

    int fa[N]; //存储每个点的祖宗节点
  
    // 初始化,假定节点编号是1~n
    for (int i = 1; i <= n; i ++ ) fa[i] = i;
    //所有元素各自独立构成集合,n棵只有根节点的树
    
    int Get(int x)    // 返回x的祖宗节点
    {
        if (fa[x] != x) fa[x] = Get(fa[x]);//路径压缩
        return fa[x];
    }

    // 合并a和b所在的两个集合:
    void Merge(int a,int b)
    {
    	fa[Get(a)] = Get(b);
	}

(2)维护size的并查集:

    int fa[N], size[N];
    //fa[]存储每个点的祖宗节点, size[]只有祖宗节点的有意义,表示祖宗节点所在集合中的点的数量
    
	// 初始化,假定节点编号是1~n
    for (int i = 1; i <= n; i ++ )
    {
        fa[i] = i;   size[i] = 1;
    }
    
    // 返回x的祖宗节点
    int Get(int x)
    {
        if (fa[x] != x) fa[x] = Get(fa[x]);
        return fa[x];
    }
    
    // 合并a和b所在的两个集合:
    void Merge(int a,int b)
    {
    	size[Get(b)] += size[Get(a)];
    	fa[Get(a)] = Get(b);
	}
    

(3)维护到祖宗节点距离的并查集:

    int fa[N], d[N];
    //fa[]存储每个点的祖宗节点, d[x]存储x到p[x]的距离
    
    for (int i = 1; i <= n; i ++ )	 // 初始化,假定节点编号是1~n
    {
        p[i] = i;    d[i] = 0;
    }
    
    int Get(int x) // 返回x的祖宗节点
    {
        if (fa[x] != x)
        {
            int u = Get(fa[x]);
            d[x] += d[fa[x]];
            fa[x] = u;
        }
        return fa[x];
    }
    
    // 合并a和b所在的两个集合:
     void Merge(int a,int b)
    {
    	 fa[Get(a)] = Get(b);
   		 d[Get(a)] = distance; // 根据具体问题,初始化find(a)的偏移量
	}
   

237. 程序自动分析

#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
int fa[N],id[N],cnt;
struct node{
    int x,y,flag;
}a[N];
bool cmp(node c,node d)
{
    return c.flag>d.flag;
}
int getid(int k)
{
    return lower_bound(id,id+cnt,k)-id;
}
int Find(int k)
{
    if(fa[k]!=k) fa[k]=Find(fa[k]);
    return fa[k];
}
void merge(int c,int d)
{
    fa[Find(c)]=Find(d);
}
int main()
{
    int t;scanf("%d",&t);
    while(t--)
    {
        int n;cnt=0;scanf("%d",&n);
        for(int i=0;i<n;++i)
        {
            scanf("%d%d%d", &a[i].x, &a[i].y,&a[i].flag);
            id[cnt++]=a[i].x;id[cnt++]=a[i].y;
        }sort(a,a+n,cmp);
        sort(id,id+cnt);cnt=unique(id,id+cnt)-id;
        for(int i=0;i<=cnt;++i)
        {
            fa[i]=i;
        }int flag=0;
        for(int i=0;i<n;++i)
        {
            if(a[i].flag==1)
            {
                int xx=getid(a[i].x),yy=getid(a[i].y);
                merge(xx,yy);
               // printf("xx:%d,yy:%d  find(xx):%d,find(yy):%d\n",xx,yy,Find(xx),Find(yy));
            }
            else 
            {
                int xx=getid(a[i].x),yy=getid(a[i].y);
                if(Find(xx)==Find(yy)) flag=1;
                //printf("xx:%d,yy:%d  find(xx):%d,find(yy):%d\n",xx,yy,Find(xx),Find(yy));
            }
        }
        if(flag==1) cout<<"NO"<<endl;
        else cout<<"YES"<<endl;
    }
}

145. 超市

#include <bits/stdc++.h>
using namespace std;
const int N = 1e4+10;
typedef long long ll;
struct node{
    int p,d;
}a[N];
int fa[N];
bool cmp(node x,node y)
{
    return x.p>y.p;
}
int Find(int k)
{
    if(fa[k]!=k) fa[k]=Find(fa[k]);
    return fa[k];
}
void merge(int x,int  y)
{
    fa[Find(x)]=Find(y);
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        int maxday=0;
        for(int i=0;i<n;++i)
        {
            cin>>a[i].p>>a[i].d;
            maxday=max(maxday,a[i].d);
        }
        sort(a,a+n,cmp);
        for(int i=1;i<=maxday;++i)
        fa[i]=i;
        ll res=0;
        for(int i=0;i<n;++i)
        {
            if(Find(a[i].d)>0) 
            {
                res+=a[i].p;
                merge(Find(a[i].d),Find(a[i].d)-1);
            }
        }
        cout<<res<<endl;
    }
   
    
}

边带权的并查集:
238. 银河英雄传说

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 3e4+10;
int fa[N],d[N],Size[N];
int Find(int k)
{
        if(fa[k]==k) return k;
        int root=Find(fa[k]);
        d[k]+=d[fa[k]];
        return fa[k]=root;
}
void merge(int x,int y)
{
        x=Find(x);y=Find(y);
        fa[x]=y;
        d[x]=Size[y];
        Size[y]+=Size[x];
}
int main()
{
        for(int i=1;i<=N;++i)
        {
                fa[i]=i;d[i]=0;Size[i]=1;
        }
   int t;cin>>t;
   while(t--)
   {
           string op;int i,j;
           cin>>op>>i>>j;
           if(op=="M")
           {
                  merge(i,j);
           }
           else if(op=="C")
           {
                   if(Find(i)!=Find(j)) cout <<"-1"<<endl;
                   else cout<<abs(d[i]-d[j])-1<<endl;
           }
   }
}

!!!239. 奇偶游戏

边带权解法

#include<bits/stdc++.h>
typedef long long LL;
const int N = 1e4+10;
using namespace std;
struct node{
    int l,r,flag;
}query[N];
int fa[N*2],d[N*2],a[N*2],cnt=0,n,m;
void read()
{
    int n;cin>>n>>m;
    for(int i=0;i<m;++i)
    {
        string s;
        cin>>query[i].l>>query[i].r>>s;
        if(s=="even") query[i].flag=0;
        else query[i].flag=1;
        a[++cnt]=query[i].l-1;a[++cnt]=query[i].r;
    }
    sort(a+1,a+cnt+1);
    cnt=unique(a+1,a+cnt+1)-a-1;
    for(int i=1;i<=cnt;++i)
    {
        fa[i]=i;
    }
}
int Find(int x)
{
    if(fa[x]==x) return x;
    int root=Find(fa[x]);
    d[x]^=d[fa[x]];
    return fa[x]=root;
}
int main()
{
    read();
    for(int i=0;i<m;++i)
    {
        int x=lower_bound(a+1,a+cnt+1,query[i].l-1)-a;
        int y=lower_bound(a+1,a+cnt+1,query[i].r)-a;
        int p=Find(x),q=Find(y);
        if(p==q)
        {
            if((d[x]^d[y])!=query[i].flag) 
            {
                cout<<i<<endl;return 0;
            }
        }else
        {
            fa[p]=q;
            d[p]=d[x]^d[y]^query[i].flag;
        }
       
    }
     cout<<m<<endl;
}

扩展域解法:

#include<bits/stdc++.h>
typedef long long LL;
const int N = 1e4+10;
using namespace std;
int fa[N*2],a[N*2],cnt=0,n,m;
struct node{
    int l,r,flag;
}query[N];
void read()
{
    int n;cin>>n>>m;
    for(int i=0;i<m;++i)
    {
        string s;
        cin>>query[i].l>>query[i].r>>s;
        if(s=="even") query[i].flag=0;
        else query[i].flag=1;
        a[++cnt]=query[i].l-1;a[++cnt]=query[i].r;
    }
    sort(a+1,a+cnt+1);
    cnt=unique(a+1,a+cnt+1)-a-1;
    for(int i=1;i<=cnt*2;++i)
    {
        fa[i]=i;
    }
}
int Find(int x)
{
    if(fa[x]==x) return x;
    return fa[x]=Find(fa[x]);
}
int main()
{
    read();
    for(int i=0;i<m;++i)
    {
        int x=lower_bound(a+1,a+cnt+1,query[i].l-1)-a;
        int y=lower_bound(a+1,a+cnt+1,query[i].r)-a;
        int x_odd=x,x_even=x+cnt;
        int y_odd=y,y_even=y+cnt;
        if(query[i].flag==0)
        {//printf("x:%d;y:%d  Find:x_odd:%d,y_even:%d\n",x,y,Find(x_odd),Find(y_even));
            if(Find(x_odd)==Find(y_even))
            {
                cout<<i<<endl;return 0;
            }
            fa[Find(x_odd)]=Find(y_odd);
            fa[Find(x_even)]=Find(y_even);
        }
        else
        {
            if(Find(x_odd)==Find(y_odd))
            {
                cout<<i<<endl;return 0;
            }
            fa[Find(x_odd)]=Find(y_even);
            fa[Find(x_even)]=Find(y_odd);
        }
    }
     cout<<m<<endl;
}

240. 食物链*

#include<bits/stdc++.h>
using namespace std;
const int N = 5e4+10;
int fa[N*3];
int Find(int x)
{
    if(x==fa[x]) return x;
    return fa[x]=Find(fa[x]);
}
int main()
{
    int n,k;cin>>n>>k;
    for(int i=1;i<=n*3;++i)
    {
        fa[i]=i;
    }int ans=0;
    for(int i=0;i<k;++i)
    {
     int d,x,y;
     cin>>d>>x>>y;
     
     if(x>n||y>n) ans++;
     else if(d==1)
     {
         if(Find(x)==Find(y+n)||Find(x)==Find(y+2*n))
         ans++;
         else 
         {
             fa[Find(x)]=Find(y);
             fa[Find(x+n)]=Find(y+n);
             fa[Find(x+2*n)]=Find(y+2*n);
         }
     }
     else if(d==2)
     {
         if(x==y||Find(x)==Find(y)||Find(x)==Find(y+n))
         {
             ans++;
         }
         else 
         {
             fa[Find(x)]=Find(y+2*n);
             fa[Find(x+n)]=Find(y);
             fa[Find(x+2*n)]=Find(y+n);
         }
     }
    }
    cout<<ans<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值