并查集+例题

定义

并查集是一种树形数据结构,实际使用数组进行存储,用于处理一些不相交集合的合并及查询问题。
主要由find和join函数组成。
find函数,查找结点的根结点

    public static int find(int k) {
        if (pre[k]==k)return k;//根节点返回
        else return pre[k]=find(pre[k]);//为此树下的所有结点的父节点都设置成根节点,降低时间复杂度
    }

join函数,将两个不相交的树合并

    public static void join(int a,int b) {
        int fa=find(a),fb=find(b);
        if (fa!=fb)pre[fa]=fb;
    }

并查集在经历过几轮查找和合并操作后时间复杂度会降到o(1)级别。
P8654 [蓝桥杯 2017 国 C] 合根植物
算是个模板题了

package 并查集;

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class P8654合根植物 {
    static int[] pre=new int[1000005];

    public static void init(int n,int m) {
        for(int i=1; i<=n*m; i++)
            pre[i]=i;
    }
    public static int find(int x) {
        if (pre[x]==x)return x;
        else return pre[x]=find(pre[x]);
    }
    public static void join(int a,int b) {
        int fa=find(a),fb=find(b);
        if (fa!=fb)pre[fa]=fb;
    }
    public static void main(String[] args) {
        int m,n,k,a,b,ans=0;
        Scanner in=new Scanner(System.in);
        m=in.nextInt();n=in.nextInt();k=in.nextInt();
        init(m,n);
        while(k--!=0){
            a=in.nextInt();b=in.nextInt();
            join(a,b);
        }
        for (int i = 1; i <= n*m; i++) {
            if (i==pre[i])ans++;
        }
        System.out.println(ans);
    }
}

P8686 [蓝桥杯 2019 省 A] 修改数组
主要运用了路径压缩来降低时间复杂度

package 并查集;


import java.io.*;

public class P8686修改数组 {
    static int[] pre = new int[2000005],vis=new int[2000005];
    public static void init(int n) {
        for (int i = 1; i <= 2000004; i++) {
            pre[i]=i;
        }
    }
    public static int find(int x) {
        if (pre[x] == x) return x;
        else return pre[x] = find(pre[x]);
    }
    public static void main(String[] args) throws IOException {
        int n;
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
        n=Integer.parseInt(in.readLine());
        init(n);
        String[] a=in.readLine().split(" ");
        for (int i = 1; i <= n; i++) {
            //未访问过
            if (vis[Integer.parseInt(a[i-1])]==0){
                out.print(a[i-1]+" ");
                vis[Integer.parseInt(a[i-1])]=1;pre[Integer.parseInt(a[i-1])]=Integer.parseInt(a[i-1])+1;
            }else {
                //访问过
                int fa = find(Integer.parseInt(a[i-1]));
                vis[fa]=1;pre[fa]=fa+1;//设置父节点为fa+1,在下次查找时,自动找到下一个能用的结点,并将所有与之相关的结点修改
                out.print(fa+" ");
            }
        }
        out.flush();
    }
}

P2949 [USACO09OPEN]Work Scheduling G

贪心+并查集

package 贪心;

import java.util.*;

public class P2949_Work_Scheduling_G {
    //并查集在运行一段时间后可以将一段数据都连起来
    static class task{
        int d,p;
    }
    static Map<Integer,Integer> m=new HashMap<>();
    static int n;

    public static int find(int x) {
        if (!m.containsKey(x)){
            m.put(x,x);
        }
        if (m.get(x)==x)return x;
        else {
            //要注意put函数返回的值是键值之前对应的值,所以不能写成注释的形式
            int t=find(m.get(x));
            m.put(x,t);
            return t;
        }
        //return m.get(x)==x?x:m.put(x,find(m.get(x)));
    }

    public static void join(int a,int b) {
        int fa=find(a),fb=find(b);
        if (fa!=fb)m.put(fa,fb);
    }
    public static void main(String[] args) {
        long ans=0;
        task t[]=new task[100005];
        Scanner in=new Scanner(System.in);
        n=in.nextInt();
        for (int i = 1; i <= n; i++) {
            t[i]=new task();
            t[i].d=in.nextInt();
            t[i].p=in.nextInt();
        }
        Arrays.sort(t, 1, 1 + n, new Comparator<task>() {
            @Override
            public int compare(task o1, task o2) {
                return o2.p-o1.p;
            }
        });
        int tt;
        for (int i = 1; i <= n; i++) {
            tt=find(t[i].d);
            //以0为结束点,当tt为0时代表当前工作无天数安排
            if (tt>=1) {
                ans += t[i].p;
                //m.put(tt, tt-1);
                join(tt,tt-1);//当前工作日以被占,指向下一个可能可用工作日,如果可用下个工作直接占领,如果不可用,则该工作日又指向下一个可能可用工作日
            }
        }
        System.out.println(ans);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值