并查集介绍

  并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。

  并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题。常常在使用中以森林来表示。
  
  两种优化方式:
  •   第一个优化是启发式合并。在优化单链表时,我们将较短的表链到较长的表尾,在这里我们可以用同样的方法,将深度较小的树指到深度较大的树的根上。这样可以防止树的退化,最坏情况不会出现。SUB-Find-Set(x)的时间复杂度为O(log N),PROBLEM-Relations时间复杂度为O(N + logN (M+Q))。SUB-Link(a,b)作相应改动。
  •   第二个优化是路径压缩。它非常简单而有效。如图所示,在SUB-Find-Set(1)时,我们“顺便”将节点1, 2, 3的父节点全改为节点4,以后再调用SUB-Find-Set(1)时就只需O(1)的时间。

  案例:

Description

  若某个家族人员过于庞大,要判断两个是否是亲戚,确实还很不容易,给出某个亲戚关系图,求任意给出的两个人是否具有亲戚关系。 规定:x和y是亲戚,y和z是亲戚,那么x和z也是亲戚。如果x,y是亲戚,那么x的亲戚都是y的亲戚,y的亲戚也都是x的亲戚。

Input

  第一行:三个整数n,m,p,(n< =5000,m< =5000,p< =5000),分别表示有n个人,m个亲戚关系,询问p对亲戚关系。 以下m行:每行两个数Mi,Mj,1< =Mi,Mj< =N,表示Mi和Mj具有亲戚关系。 接下来p行:每行两个数Pi,Pj,询问Pi和Pj是否具有亲戚关系。

Output

  P行,每行一个’Yes’或’No’。表示第i个询问的答案为“具有”或“不具有”亲戚关系。
 
//    并查集

import java.util.Scanner;

public class Main{
    
    static int tree[];
    static int n;
    static int m;
    static int relationOne;
    static int relationTwo;
    
    public static int findFather(int node){
        if(node!=tree[node]){
            return tree[node]=findFather(tree[node]);
        }
        return node;
    }
    
    public static void main(String args[]){
        
        Scanner reader=new Scanner(System.in);
        n=reader.nextInt();
        m=reader.nextInt();
        tree=new int[n+1];
        relationOne=reader.nextInt();
        relationTwo=reader.nextInt();
        
        for(int i=1;i<=n;i++){
            tree[i]=i;
        }
        
        for(int i=1;i<=m;i++){
            int one=reader.nextInt();
            int two=reader.nextInt();
            int oneFather=findFather(one);
            int twoFather=findFather(two);
            if(oneFather!=twoFather){    //父结点不同
                if(oneFather>twoFather){    //指向更大的结点
                    tree[twoFather]=oneFather;
                }else{
                    tree[oneFather]=twoFather;
                }
            }
        }
        
        int relationOneFather=findFather(relationOne);
        int relationTwoFather=findFather(relationTwo);
        if(relationOneFather==relationTwoFather){
            System.out.println("YES");
        }else{
            System.out.println("NO");
        }
        
    }
    
}

 

posted @ 2019-04-13 20:59  一转身已万水千山  阅读(162)  评论(0编辑  收藏  举报