并查集学习

文章介绍了并查集的基本概念,包括初始化、查询和合并操作,以及路径压缩后的朴素并查集。接着讨论了带权并查集的两种类型——size并查集(用于计算连通块点数量)和dist并查集(用于处理食物链问题),详细阐述了它们的查询和合并方法。在dist并查集中,文章解释了为何只需更新合并集合的根节点距离即可。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

大佬的讲解:简洁而优美的结构 - 并查集 | 一文吃透 “带权并查集” 不同应用场景 | “手撕” 蓝桥杯A组J题 - 推导部分和 - 掘金 (juejin.cn)

路径压缩后的朴素并查集:

初始化:

fa[i]:i的根节点

rank[i]:以i为根节点的子树深度

初始化:

  void init(int n)
  {
   for(int i=0;i<n;i++)
    {
rank[i]=1;
fa[i]=i;
}
}

查询:查询两个元素是否在同一个集合

   int find(int x)
{   if(fa[x]==x)return x;//本身就是根节点
   return fa[x]=find(fa[x]); //使当前节点在本次查找根节点后,直接记录根节点,//避免重复计算
}

合并:两个不相交的集合合并成一个集合

  void merge(int i,int j)
{
      int x=find(i),y=find(j);
      if(rank[x]<=rank[y])
          fa[x]=fa[y];
     else 
           fa[y]=fa[x];
     if(x!=y&&rank[x]==rank[y])
       {rank[y]++;}
}

//不用改变rank,原因:两个集合存在一个rank大的集合,另外一个集合合并到该集合中后成为子树,大集合rank值未发生变化

带权并查集:

带权并查集:分为 size并查集dist并查集

size并查集:表示并查集所含的结点数

dist并查集:表示从当前点到父节点的距离

size并查集:一般用于计算连通块中点的数量的题目

p[]表示每个点的祖宗节点

size[]只有祖宗节点的size有意义,表示祖宗节点所在的集合中点的数量

查询:

   int find(int x)
{
   if(x!=p[x]) p[x]=find(p[x]); //路径压缩,p[x]直接记录祖宗是谁
   
   return p[x];
}

合并:

  bool merge(int x,int y)
{
   int X=find(x);
   int Y==find(y);
if(X==Y) return false;
  size[x]+=size[y];//不是用一个祖宗,合并成同一个集合,并更新size值
   p[y]=x;//没有判断谁的集合更大,直接归为一个集合
}

dist并查集:用于食物链的内容

初始化

int p[N],dist[N]//dist记录到祖宗的距离

for(int i=1;i<=n;i++)
 {
p[i]=i;
 dist[i]=0;
}

并查集查询操作:

查询的过程中跟新路径

int find(int x)
{
   if(x!=p[x])
{
  int root=find(p[x]);
  dist[x]+=dist[p[x]];
  p[x]=root;
}
return p[x];
}

合并操作

 boolmerge(int x,int y)
{
  int X=find(x);
int Y=find(y);
if(X==Y) return false;
 p[x]=y;
dist[x]+=(dist[y]+1);//加上另外一个集合的总个数
return true;
}

为什么y集合合并到x集合,只需要更新y集合的根节点距离新集合根节点的距离,而不需要将y集合内的所有节点距离新根节点的距离全部更新呢?

这是因为:只要后续操作中,需要对原y集合内的某个节点进行查询操作的时候,查询操作便会对此点所在路径上的所有点全部进行数据的更新,即相当于原y集合内的节点的p[x]和_dist[x]都会更新为指向合并后新的根节点和距离新根节点的距离

蓝桥杯:推导部分和


import java.util.*;
public class Main {
    static int n,m,q;
    static long dist[];
    static int fa[];
    public static void main(String args[])
    {
        Scanner in=new Scanner(System.in);
        n=in.nextInt();
        m=in.nextInt();
        q=in.nextInt();
        fa=new int[n+1];
        dist=new long[n+1];
        init(fa,dist);
        
        while(m!=0)
        {
            int l,r;
            long s;
            l=in.nextInt();
            r=in.nextInt();
            s=in.nextLong();
            
            merge(l-1,r,s);
            m--;
        }
        
        while(q!=0)
        {
            int l,r;
            l=in.nextInt();
            r=in.nextInt();
            if(find(l-1)!=find(r)) System.out.println("UNKNOWN");
            else System.out.println(dist[l-1]-dist[r]);
            q--;
        }
    }
    public static int find(int x)
    {
        if(x==fa[x]) return x;
            int root=find(fa[x]);
            dist[x]+=dist[fa[x]];
            fa[x]=root;
        return fa[x];
    }
    public static void init(int fa[],long dist[])
    {
      for(int i=1;i<=n;i++)
      {
          fa[i]=i;
          dist[i]=0;
      }
    }
    
    public static boolean merge(int x,int y,long value)
    {
         int u=find(x);
         int v=find(y);
        if(u==v) return false;
        dist[u]=dist[y]+value-dist[x];//dist[i]表示i节点到祖宗节点的距离
        //value-dist[x]表示 原来节点到新要合并节点的距离-原来节点到原来根节点的距离,即自己集合的根节点到新集合根节点的距离
        //dist[y]表示合并到新集合的合并点到该集合跟节点的距离
        fa[u]=v;
        return true;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值