AcWing 算法基础 并查集

梦想与她 提交于 2020-03-11 13:00:08

并查集

并查集解决的问题

  1. 将两个集合合并
  2. 询问两个元素是否在一个集合中

基本原理

用树的形式来维护每个集合,树根的编号就是整个集合的编号,每个节点存储他的父节点,p[x]表示x的父节点

问题1,如何判断树根:if(p[x] == x);
问题2,如何求x集合的编号: while(p[x] != x) x = p[x];
问题3,如何合并两个集合,px是x集合的编号,py是y的集合编号,令p[x]=y。

问题2优化:某个节点找到根节点之后,把整个路径上的点直接指向根节点。

例题

AcWing 836 合并集合
一共有n个数,编号是1~n,最开始每个数各自在一个集合中。
现在要进行m个操作,操作共有两种:

  1. “M a b”,将编号为a和b的两个数所在的集合合并,如果两个数已经在同一个集合中,则忽略这个操作;
  2. “Q a b”,询问编号为a和b的两个数是否在同一个集合中;
import java.util.*;
class UnionSet{
    private final int N = 100010;
    private int[] p = new int[N];
    public UnionSet(){
        for(int i = 0; i < N; i++){
            p[i] = i;
        }
    }
    // 找到该节点的root节点
    public int find(int val){
        int temp = val;
        while(p[temp] != temp){
            temp = p[temp];
        }
        p[val] = temp;
        return temp;
    }
    public void union(int a,int b){
        int pa = find(a);
        int pb = find(b);
        if(pa != pb) p[pa] = pb;
    }
    public boolean isunion(int a,int b){
        int pa = find(a);
        int pb = find(b);
        if(pa != pb) return false;
        else return true;
    }
}
public class Main{
    public static void main(String[] args){
        Scanner Reader = new Scanner(System.in);
        int n = Reader.nextInt();
        int m = Reader.nextInt();
        UnionSet us = new UnionSet();
        while(m-- != 0){
            String temp = Reader.next();
            if(temp.equals("M")){
                int v1 = Reader.nextInt(),v2 = Reader.nextInt();
                us.union(v1,v2);
            }
            if(temp.equals("Q")){
                if(us.isunion(Reader.nextInt(),Reader.nextInt()))
                    System.out.println("Yes");
                else
                    System.out.println("No");
            }
        }
    }
}

AcWing 837 连通块中点的数量
给定一个包含n个点(编号为1~n)的无向图,初始时图中没有边。
现在要进行m个操作,操作共有三种:

  1. “C a b”,在点a和点b之间连一条边,a和b可能相等;
  2. “Q1 a b”,询问点a和点b是否在同一个连通块中,a和b可能相等;
  3. “Q2 a”,询问点a所在连通块中点的数量;
import java.util.*;
class UnionSet{
    private final int N = 100010;
    private int[] p = new int[N];
    private int[] size = new int[N];
    public UnionSet(){
        for(int i = 0; i < N; i++){
            p[i] = i;
            size[i] = 1;
        }
    }
    // 找到该节点的root节点
    public int find(int val){
        int temp = val;
        while(p[temp] != temp){
            temp = p[temp];
        }
        p[val] = temp;
        return temp;
    }
    public void union(int a,int b){
        int pa = find(a);
        int pb = find(b);
        if(pa != pb) {
            p[pa] = pb;
            size[pb] += size[pa];
        }
    }
    public boolean isunion(int a,int b){
        int pa = find(a);
        int pb = find(b);
        if(pa != pb) return false;
        else return true;
    }
    public int size(int a){
        return size[find(a)];
    }
}
public class Main{
    public static void main(String[] args){
        Scanner Reader = new Scanner(System.in);
        int n = Reader.nextInt();
        int m = Reader.nextInt();
        UnionSet us = new UnionSet();
        while(m-- != 0){
            String temp = Reader.next();
            if(temp.equals("C")){
                int v1 = Reader.nextInt(),v2 = Reader.nextInt();
                us.union(v1,v2);
            }
            if(temp.equals("Q1")){
                if(us.isunion(Reader.nextInt(),Reader.nextInt()))
                    System.out.println("Yes");
                else
                    System.out.println("No");
            }
            if(temp.equals("Q2")){
                System.out.println(us.size(Reader.nextInt()));
            }
        }
    }
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!