并查集学习

并查集

并查集是一种森林或树数据结构,常用来处理不相交集合的合并、查询问题。并查集和深度优先搜索、广度优先搜索是图的环的三个查询方法。

并查集学习_第1张图片

  • 如上图所示,我们通常将并查集初始化为 n n n个 单节点集合,然后根据题目中提供数据对并查集进行合并,最后可能构成一棵树(连通图)或者一个森林(非连通图)。

  • 采用并查集处理的问题一般都有如下特点:

    1. 数据量极大,用其他数据结构往往空间复杂度难以接受。
    2. 使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果。
  • 在处理图的连通性问题中也可以采用并查集问题,一般多用于求数量问题。

需要注意的是,并查集并不能像深度优先(DFS)和广度优先(BFS)那样得到图的遍历路径。

例题如下:

Alice 和 Bob 共有一个无向图,其中包含 n 个节点和 3 种类型的边:

类型 1:只能由 Alice 遍历。
类型 2:只能由 Bob 遍历。
类型 3:Alice 和 Bob 都可以遍历。
给你一个数组 edges ,其中 edges[i] = [typei, ui, vi] 表示节点 ui 和 vi 之间存在类型为 typei 的双向边。请你在保证图仍能够被 Alice和 Bob 完全遍历的前提下,找出可以删除的最大边数。如果从任何节点开始,Alice 和 Bob 都可以到达所有其他节点,则认为图是可以完全遍历的。

返回可以删除的最大边数,如果 Alice 和 Bob 无法完全遍历图,则返回 -1 。

来源:力扣(LeetCode)
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

  • 根据题中描述,我们可以了解到,当在两点之间有type3和任意一种边时,我们可以将非type3的边删除,另一种情况是,当存在环时,我们可以删除一条边。
  • 保证两人都能都完全遍历,我们可以这个条件看做两人的并查集最后都是一棵树,即只有一个连通分量的情况,如上图所示。
  • 值的注意的是,这道题中,我们必须先将公共边加入二者的并查集中并处理。
  • 我们在计算可删除边的数量时,首先判断当前边是否已经在连通分量中,如果已经存在,我们可以删掉这个边。因为公共边的删除会影响两个人的连通性,所以如果我们需要先单独处理公共边,之后删除二者的单独边不会对对方产生影响。同时,我们需要删除尽可能多的边,二人达到一个结点需要两条独占边或者一条公共边,在数量上考虑我们也需要先处理公共边。
public int maxNumEdgesToRemove(int n, int[][] edges) {
        Union djsa = new Union(n);
        Union djsb = new Union(n);
        int ans = 0;
        for(int[] item : edges){

            //common
            if(item[0] == 3){
            	//when edge is not in two union , djsa and djsb add the edge, otherwise, ans++
                if(!djsa.uniont(item[1]-1,item[2]-1)){
                	//edge is in djsa and djsb
                    ans++;
                }else{
                //edge is not in djsa and djsb
                    djsb.uniont(item[1]-1,item[2]-1);
                }
                
            }
        }
        for(int[] item : edges){

            //type1
            if(item[0] == 1){
                if(!djsa.uniont(item[1]-1,item[2]-1)){
                    ans++;
                }
                continue;
            }

            //type2
            if(item[0] == 2){
            	//surplus edge should be removed
                if(!djsb.uniont(item[1]-1,item[2]-1)){
                //the edge has in the union
                    ans++;
                }
                continue;
            }

            
        }
        if(djsa.count!=1 || djsb.count != 1){
            //one person can't arrive some point
            return -1;
        }
        return ans;
    }

并查集模板

public class Union{
       /**
       *并查集模板
       **/

       //根节点集
       private int[] parent; 
        //连通分量中结点数
       private int[] size;
       //连通分量数目
       int count;

       public Union(int n){
           size = new int[n];
           Arrays.fill(size,1);
           parent = new int[n];
           for(int i=0; i < n; i++){
               this.parent[i] = i;
           }
           this.count = n;
       }
       //找到根节点
       public int find(int index){
           return index == this.parent[index] ? index : (parent[index] = find(this.parent[index]));
       }
		//将两个不在同一个连通分量的点放到一起
       public boolean uniont(int index1,int index2){
           int root1 = find(index1);
           int root2 = find(index2);
           if(root1 == root2){
           //有相同的根节点,返回false
               return false;
           }
           
           this.parent[root1] = root2;
           this.size[root2] += size[root1];
           this.count--;
           return true;
       }


   }

你可能感兴趣的:(计算机,后端相关,刷题笔记,算法,数据结构,java)