并查集(union find)总结

(一)概念

并查集的基本概念见博客:https://www.cnblogs.com/xzxl/p/7226557.html

(二)用途

高效解决连接问题

  • 迷宫
  • 多节点的连接状态
  • 集合类的实现

(三)通用模板

三要素:

  • 需要记录每个结点的父结点
  • 找到每个结点的祖先结点
  • 将两个结点的祖先结点合并

代码:

public class Template {
    int[] pre;
    
    public void init(int len){ //初始化pre数组
        pre = new int[len];
        for(int i=0;i<len;i++)
            pre[i] = i;
    }
    
    public int find(int x){ //查找x的祖先是谁
        int p,tmp;
        p=x;
        while(x!=pre[x]) //一级一级去找祖先
            x = pre[x];
        //x变成祖先了,各下级直接指向祖先,说明下级与祖先有路径
       while(p!=x){ //路径压缩
            tmp=pre[p];
            pre[p]=x;
            p=tmp;
        }
        return x;
    }
    
    public void union(int x,int y){  //将两节点的祖先合并到一个祖先
        int fx = find(x);
        int fy = find(y);
        if(fx!=fy){
            pre[fx] = fy;
        }
    }
    
    public boolean isSame(int x,int y){ //判断两节点的祖先是否相同
        return find(x)==find(y);
    }
    
}

(四)精选例题

(1)Leetcode 547 Friend Circles

题意:现存在N个学生,学生与学生之间存在着朋友圈,若A是B的朋友,B是C的朋友,那么我们认为A,B,C在同一个朋友圈中,现在给定NxN的矩阵A,A[i][j]=1代表i和j是朋友。问总共有几个朋友圈?

输入:A = [[1,1,0],[1,1,0],[0,0,1]]
输出:2
输入:A = [[1,1,0],[1,1,1],[0,1,1]]
输出:1

public class Friend_Circles_547 {
    public class UnionFind{
        int[] pre; //记录x的上级是pre[x]
        int count;
     
        public void init(int n){
            pre = new int[n];
            for(int i=0;i<n;i++)
                pre[i] = i;
        }

        public int find(int x){
            while(x!=pre[x]) //一级一级去找祖先,x变成祖先了
                x = pre[x];
            return x;
        }

        public void union(int x,int y){  //将祖先合并,在这来来初始化
            int fx = find(x);
            int fy = find(y);
            if(fx!=fy){
                pre[fx] = fy;
                count--;  //拥有共同祖先时合并两个朋友圈,数量减一
            }
        }
        
    }
    
    public int findCircleNum(int[][] M) { 
        if(M==null || M.length==0)
            return 0;
        UnionFind uf = new UnionFind();
        int len = M.length;
        uf.init(len);
        uf.count = len;
        for(int i=0;i<len;i++){
            for(int j=i+1;j<len;j++){
                if(M[i][j]==1){
                    uf.union(i, j);
                }
            }
        }
    return uf.count;
}
}
(2)Leetcode 684 Redundant Connection

题意:给定一个数组A来表示一个图,A[i] = [u,v]表示在节点u,v之间存在边。
现在确保这个图(本身不是树)恰好通过只删除一条边可以变成树,请找出删除的边,若有多条,返回数组中最后出现的那一个。

输入:A = [[1,2],[1,3],[2,3]]
输出:[2,3]
输入:A = [[1,2], [2,3], [3,4],[1,4], 1,5]]
输出:[1,4]

public class Redundant_Connection_684 {
    public class UnionFind{
       int[] pre;
        public void init(int len){
            pre = new int[len+5];
            for(int i=0;i<len+5;i++)
                pre[i] = i;
        }

        public int find(int x){
            while(x!=pre[x]) //一级一级去找祖先,x变成祖先了
                x = pre[x];
            return x;
        }

        public boolean union(int x,int y){  //将祖先合并,在这来来初始化
            int fx = find(x);
            int fy = find(y);
            
            if(fx==fy){
                return false;  //已经有共同祖先了,不需要合并就代表有圈
            }else{
                pre[fx] = fy;
                return true;

            }
        }
    }
    
    public int[] findRedundantConnection(int[][] edges) {
        UnionFind uf = new UnionFind();
        int len = edges.length;
        uf.init(len);
        int[] res = new int[2];
        for(int i=0;i<len;i++){
            boolean flag = uf.union(edges[i][0], edges[i][1]);
            if(flag==false){
                res[0]=edges[i][0];
                res[1]=edges[i][1];
                break;
        }
    }
        
        return res;
    }
}
(3)Leetcode 959 Regions Cut By Slashes

题意:给定一个NxN的矩阵,矩阵是由1x1的小格子组成,每个小格子中可能包含’/’, ’\’, ’’三种情况,返回矩阵中的连通区域。

输入:A = [“ /”, “/ ”]
输出:2
输入:A = [“\/”, “/\”]
输出:4

public class Regions_Cut_By_Slashes_959 {
    
    public class UnionFind{
        int[] pre;
        
        public void init(int len){ //每一个正方形两对角线变成4个三角形,进行编号
            pre = new int[len*len*4];
            for(int i=0;i<len*len*4;i++)
                pre[i] = i;
        }

        public int find(int x){
            while(x!=pre[x]) //一级一级去找祖先,x变成祖先了
                x = pre[x];
            return x;
        }

        public void union(int x,int y){  //将祖先合并,在这来来初始化
            int fx = find(x);
            int fy = find(y);
            if(fx!=fy){
                pre[fx] = fy;
            }
        }
    }
    
    public int regionsBySlashes(String[] grid) {
        int n = grid.length; //N*N的网格
        UnionFind uf = new UnionFind();
        uf.init(n);
        int res = 0;
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                char cur = grid[i].charAt(j); 
                int root = (i*n+j)*4;
                if (cur != '\\'){
                    uf.union(root + 0, root + 1);
                    uf.union(root + 2, root + 3);
                }
                if (cur != '/') {
                    uf.union(root + 0, root + 2);
                    uf.union(root + 1, root + 3);
                }
                // north south
                if (i + 1 < n)
                    uf.union(root + 3, (root + 4 * n) + 0);
                if (i - 1 >= 0)
                    uf.union(root + 0, (root - 4 * n) + 3);
                // east west
                if (j + 1 < n)
                    uf.union(root + 2, (root + 4) + 1);
                if (j - 1 >= 0)
                    uf.union(root + 1, (root - 4) + 2);
            }
        }
        for (int x = 0; x < 4 * n * n; x++) {
            if (uf.find(x) == x)
                res++;
        }
        return res;
    }
}

(4)Leetcode 765 Couples Holding Hands (待更)

有N对夫妇随机坐在2*N个座位上,现在要使每对夫妇都能牵手(相邻即可),我们需要交换一些人的座位,问最少需要交换多少次才能满足题意。
夫妇使用数字表示,(0,1)是一对夫妇,(2,3)是一对夫妇,以此类推。
提示:最多存在30对夫妇。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值