蓝桥杯JAVA-27.普通并查集模板(JAVA实现)

个人博客
www.tothefor.com
蓝桥杯复习知识点汇总

目录

碰到了一个感觉可以用并查集解决的题,所以就复习一下普通并查集吧。因为学过也比较熟悉,所以本文主要是记录一下模板。

题目链接(大致思路:从1开始,把经过的点都合并起来,最后看要求的点的根节点是否是1即可。AC代码见最后。)

并查集(Union-find Sets)是一种非常精巧而实用的数据结构,它主要用于处理一些不相交集合的合并问题。一些常见的用途有求连通子图、求最小生成树的 Kruskal 算法和求最近公共祖先(Least Common Ancestors, LCA)等。

它用于处理一些不交集的 合并 及 查询 问题。 它支持两种操作:

  • 查找(Find):确定某个元素处于哪个子集;
  • 合并(Union):将两个子集合并成一个集合。

在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合(即自己单独一个集合),然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。

普通并查集模板

并查集(带路径压缩):

    //并查集(路径压缩)
    
    //par数组用来存储根节点,par[x]=y表示x的根节点为y
    static int[] par = new int[10005];
    //初始化
    public static void init(int n){
        for (int i = 1; i <= n; i++) {
            par[i]=i;
        }
    }
    //查找x所在集合的根
    public static int find(int x){
        if(par[x]!=x) par[x]=find(par[x]); //递归返回的同时压缩路径
        return par[x];
    }
    //合并x与y所在集合
    public static void unite(int x,int y){
        int tx = find(x);
        int ty = find(y);
        if(tx!=ty){ //不是同一个根,即不在同一个集合,就合并
            par[tx]=ty;
        }
    }

路径压缩理解:

从开始的这样

变成这样

再变成这样

最后变成了这样

普通并查集(统计集合中元素个数)

用来计算每一个集合中元素的个数有多少。在普通并查集的基础上加一个数组来统计每一个集合中元素的个数即可。在初始化的时候每一个集合中的个数都为1,在合并的时候将子节点中的元素个数加到根节点的元素个数中去。

就只是在普通并查集的基础上修改两个地方:

    //初始化
    public static void init(int n){
        for (int i = 1; i <= n; i++) {
            par[i]=i;
            sum[i]=1; //每一个集合开始都是1
        }
    }
    
    //合并x与y所在集合
    public static void unite(int x,int y){
        int tx = find(x);
        int ty = find(y);
        if(tx!=ty){
            par[tx]=ty; //让ty成为tx的根节点
            sum[ty]+=sum[tx]; //让根节点加上子节点中的元素个数
        }
    }

完整代码:

    //并查集(路径压缩)
    static int[] par = new int[10005];
    //sum数组用来存储每一个集合中的元素个数
    static int[] sum = new int[10005];
    //初始化
    public static void init(int n){
        for (int i = 1; i <= n; i++) {
            par[i]=i;
            sum[i]=1;
        }
    }
    //查找x所在集合的根
    public static int find(int x){
        if(par[x]!=x) par[x]=find(par[x]); //递归返回的同时压缩路径
        return par[x];
    }
    //合并x与y所在集合
    public static void unite(int x,int y){
        int tx = find(x);
        int ty = find(y);
        if(tx!=ty){
            par[tx]=ty;
            sum[ty]+=sum[tx];
        }
    }

AC代码

import javafx.util.Pair;

import java.io.*;
import java.text.DecimalFormat;
import java.util.*;

/**
 * @Author DragonOne
 * @Date 2021/12/5 21:27
 * @墨水记忆 www.tothefor.com
 */
public class Main {
    public static BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    public static BufferedWriter out = new BufferedWriter(new OutputStreamWriter(System.out));
    public static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
    public static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));

    public static int[] par = new int[100005];

    public static void main(String[] args) throws Exception {
        cin.nextToken();
        int n = (int) cin.nval;
        init(n);
        cin.nextToken();
        int t = (int) cin.nval;
        int[] f = new int[n + 5];
        for (int i = 1; i <= n - 1; ++i) {
            cin.nextToken();
            f[i] = (int) cin.nval;
        }
        for(int i=1;i<n;){
            int sm = i + f[i];
            if(sm<=n){
                unite(i,sm);
            }
            i = i + f[i];
        }
        if(par[t]==1){
            cout.println("YES");
            cout.flush();
        }else{
            cout.println("NO");
            cout.flush();
        }

        closeAll();
    }

    public static void init(int x) {
        for (int i = 1; i <= x; ++i) {
            par[i] = i;
        }
    }

    public static int find(int x) {
        if (par[x] != x) par[x] = find(par[x]);
        return par[x];
    }

    public static void unite(int x, int y) { //注意顺序,谁是谁的根节点
        int tx = find(x);
        int ty = find(y);
        if (tx != ty) {
            par[ty] = tx;
        }
    }

    public static void closeAll() throws Exception {
        cout.close();
        in.close();
        out.close();
    }

}

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值