图论之并查集模板和经典例题分析(Java语言描述)

Posted nuist__NJUPT

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图论之并查集模板和经典例题分析(Java语言描述)相关的知识,希望对你有一定的参考价值。

图论之并查集模板和经典例题分析

  • 并查集:
  • 在一些有N个元素的集合应应用中,我们通常在开始时候让每个元素构成一个单元素的集合,然后按照一定的顺序将属于同一组的元素所在集合合并,期间要反复查找每个元素的所在集合。
  • 主要方法:
  • find:查询某个数据属于哪个分组
  • union:合并两个分组到同一个集合

1-基础模板


public class Main 
    static int [] parent ;
    public Main(int N)
        parent = new int [N] ;
        for(int i=0; i<N; i++)
            parent[i] = i ;
        
    
    public int find(int x)
        if(parent[x] != x)
            parent[x] = find(parent[x]) ;
        
        return parent[x] ;
    

    public void union(int x, int y)
        parent[find(x)] = find(y) ;
    
    


2-优化模板

import java.util.Arrays;

public class Main2 
    static int [] parent ;
    static int [] rank ;
    public Main2(int N)
        parent = new int [N] ;
        rank = new int [N] ;
        for(int i=0; i<N; i++)
            parent[i] = i ;
        
        Arrays.fill(rank, 1) ;
    
    public int find(int x)
        if(x != parent[x])
            parent[x] = find(parent[x]) ;
        
        return parent[x] ;
    
    public void union(int x, int y)
        int rootX = find(x) ;
        int rootY = find(y) ;
        if(rootX == rootY)
            return ;
        
        if(rank[rootX] > rank[rootY])
            parent[rootY] = rootX ;
        else if(rank[rootX] < rank[rootY])
            parent[rootX] = rootY ;
        else
            parent[rootX] = rootY ;
            rank[rootX] ++ ;
        
    


3-经典例题

  • 1-亲戚问题
  • 或许你并不知道,你的某个朋友是你的亲戚。他可能是你的曾祖父的外公的女婿的外甥女的表姐的孙子。如果能得到完整的家谱,
  • 判断两个人是否是亲戚应该是可行的,但如果两个人的最近公共祖先与他们相隔好几代,使得家谱十分庞大,
  • 那么检验亲戚关系实非人力所能及。在这种情况下,最好的帮手就是计算机。为了将问题简化,你将得到一些亲戚关系的信息,
  • 如Marry和Tom是亲戚,Tom和Ben是亲戚,等等。从这些信息中,你可以推出Marry和Ben是亲戚。
  • 请写一个程序,对于我们的关于亲戚关系的提问,以最快的速度给出答案。
  • 输入
  • 输入由两部分组成。
  • 第一部分以N,M开始。N为问题涉及的人的个数(1≤N≤20000)。这些人的编号为1,2,3,…, N。下面有M行(1≤M≤1000000), 每行有两个数ai,bi,表示已知ai 和bi是亲戚。
  • 第二部分以Q开始。以下Q行有Q个询问(1≤ Q ≤1000000),每行为ci,di,表示询问ci 和di是否为亲戚。
  • 输出
  • 对于每个询问ci,di,输出一行:若ci 和di为亲戚,则输出“Yes”,否则输出“No”。
  • 样例输入
  • 10 7
  • 2 4
  • 5 7
  • 1 3
  • 8 9
  • 1 2
  • 5 6
  • 2 3
  • 3
  • 3 4
  • 7 10
  • 8 9
  • 样例输出
  • Yes
  • No
  • Yes

import java.util.Scanner;
public class Main3 
    static int N ;
    static int M ;
    static int Q ;
    static int [] parent ;
    static int [] rank  ;
    static String [] res ;

    public static void main(String[] args) 
        Scanner input = new Scanner(System.in) ;
        N = input.nextInt() ;
        M = input.nextInt() ;

        init(N)  ;
        for(int i=0; i<M; i++)
            int a = input.nextInt() ;
            int b = input.nextInt() ;
            merge(a, b) ;
        
        Q = input.nextInt() ;
        res = new String[Q] ;
        for(int i=0; i<Q; i++)
            int a = input.nextInt() ;
            int b = input.nextInt() ;

           // res[i] = find(a) == find(b) ? "Yes" : "No" ;
            if(find(a) == find(b))
                res[i] = "Yes" ;
            else
                res[i] = "No" ;
            
        
        for(int i=0; i<res.length; i++)
            System.out.println(res[i]);
        
    

    private static void init(int N)  //初始化集合元素和等级
        parent = new int [N+1] ;
        rank = new int [N+1] ;
        for(int i=1; i<=N; i++)
            parent[i] = i ;
            rank[i] = 1 ;
        
    

    private static void merge(int a, int b)  //两个集合合并成一个集合
        int x = find(a) ;
        int y = find(b) ;
        if(x == y)
            return ;
        
        if(rank[x] > rank[y])
            parent[y] = x ;
        else if(rank[x] < rank[y])
            parent[x] = y ;
        else
            parent[x] = y ;
            rank[y] ++ ;
        
    

    private static int find(int x) //判别是否属于同一个集合
        if(x != parent[x])
            parent[x] = find(parent[x]) ;
        
        return parent[x] ;
    


2-数码世界

  • 题目描述
  • 有一个叫做“数码世界”奇异空间,在数码世界里生活着许许多多的数码宝贝,其中有些数码宝贝之间可能是好朋友,
  • 并且数码宝贝世界有两条不成文的规定:
  • 第一,数码宝贝A和数码宝贝B是好朋友等价于数码宝贝B与数码宝贝A是好朋友
  • 第二,如果数码宝贝A和数码宝贝C是好朋友,而数码宝贝B和数码宝贝C也是好朋友,那么A和B也是好朋友
  • 现在给出这些数码宝贝中所有好朋友的信息,问:可以把这些数码宝贝分成多少组,满足每组中的任意两个
  • 数码宝贝都是好朋友,而且任意两组之间的数码宝贝都不是好朋友
  • 输入格式
  • 输入的第一行有两个正整数n(n <= 100)和m(m <= 100),分别表示数码宝贝的个数和好朋友的组数,
  • 其中数码宝贝编号为1~n
  • 接下来有m行,每行两个正整数a和b,表示数码宝贝a和数码宝贝b是好朋友
  • 输出格式
  • 输出一个整数,表示这些数码宝贝可以分成的组数
  • 样例输入
  • 7 5
  • 1 2
  • 2 3
  • 3 1
  • 1 4
  • 5 6
  • 样例输出
  • 3


import java.util.Scanner;

public class Main4 
    static int n ;
    static int m ;
    static int [] fa ;
    static int [] rank ;
    static int count = 0 ;
    public static void main(String[] args) 
        Scanner input = new Scanner(System.in) ;
        n = input.nextInt() ; //数码宝贝个数
        m = input.nextInt() ; //好朋友组数
        init(n) ;
        for(int i=0; i<m; i++)
            int x = input.nextInt() ;
            int y  = input.nextInt() ;
            merge(x, y) ;
        
        for(int  i=1; i<=n; i++) //集合根节点的数目就是集合个数,即组数
            if(find(i) == i)
                count ++ ;
            
        
        System.out.println(count);
    

    private static void init(int n) 
        fa = new int [n+1] ;
        rank = new int [n+1] ;
        for(int i=1; i<=n; i++)
            fa[i] = i ;
            rank[i] = 1 ;
        
    

    private static void merge(int x, int y)
        int rootX = find(x) ;
        int rootY = find(y) ;
        if(rootX == rootY)
            return ;
        
        if(rank[rootX] > rank[rootY])
            fa[rootY] = rootX ;
        else if(rank[rootX] < rank[rootY])
            fa[rootX] = rootY ;
        else
            fa[rootX] = rootY ;
            rank[rootY]++ ;
        
    

    private static int find(int x) 
        if(x != fa[x])
            fa[x] = find(fa[x]) ;
        
        return fa[x] ;
    


3-畅通工程

  • 1.某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条道路直接连通的城镇。
  • 省政府“畅通工程”的目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,只要互相间接通过道路可达即可)。
  • 问最少还需要建设多少条道路?
  • Input
  • 测试输入包含若干测试用例。每个测试用例的第1行给出两个正整数,分别是城镇数目N ( < 1000 )和道路数目M;
  • 随后的M行对应M条道路,每行给出一对正整数,分别是该条道路直接连通的两个城镇的编号。为简单起见,城镇从1到N编号。
  • 注意:两个城市之间可以有多条道路相通,也就是说
  • 3 3
  • 1 2
  • 1 2
  • 2 1
  • 这种输入也是合法的
  • 当N为0时,输入结束,该用例不被处理。
  • Sample Input
  • 4 2
  • 1 3
  • 4 3
  • 3 3
  • 1 2
  • 1 3
  • 2 3
  • 5 2
  • 1 2
  • 3 5
  • 999 0
  • 0
  • Sample Output
  • 1
  • 0
  • 2
  • 998

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Main5 
    static int N = 1  ;
    static int M ;
    static int [] fa ;
    static int [] rank ;
    static List<Integer> list = new ArrayList<>() ;

    public static void main(String[] args) 
        Scanner input = new Scanner(System.in) ;

        while(N != 0)
            N = input.nextInt() ;
            if(N == 0)
                break ;
            
            M = input.nextInt() ;
            init(N) ;
            for(int i=0; i<M; i++)
                 int x = input.nextInt() ;
                 int y = input.nextInt() ;
                 merge(x, y) ;
            
            int cnt = 0 ;
            for(int i=1; i<=N; i++)
                if(i == find(i))
                    cnt ++ ;
                
            
            list.add(cnt-1) ; //还需要建设道路数等于当前集合数减1
        
        for(int i=0; i<list.size(); i++)
            System.out.println(list.get(i));
        
    
    private static void init(int n) 
        fa = new int [n+1] ;
        rank = new int [n+1] ;
        for(int i=1; i<=n; i++)
            fa[i] = i ;
            rank[i] = 1 ;
        
    

    private static void merge(int x, int y)
        int rootX = find(x) ;
        int rootY = find(y) ;
        if(rootX == rootY)
            return ;
        
        if(rank[rootX] > rank[rootY])
            fa[rootY] = rootX ;
        else if(rank[rootX] < rank[rootY])
            fa[rootX] = rootY ;
        else
            fa[rootX] = rootY ;
            rank[rootY]++ ;
        
    

    private static int find(int x) 
        if(x != fa[x])
            fa[x] = find(fa[x]) ;
        
        return fa[x] ;
    


以上是关于图论之并查集模板和经典例题分析(Java语言描述)的主要内容,如果未能解决你的问题,请参考以下文章

hiho14 无间道之并查集图论--并查集

带权并查集(含种类并查集)经典模板 例题:①POJ 1182 食物链(经典)②HDU - 1829 A bug's life(简单) ③hihoCoder 1515 : 分数调查(示例代码(代

ACM入门之并查集

ybtoj并查集例题2程序自动分析

0050数据结构之并查集

数据结构之并查集