并查集1111

并查集应用

并查集一般可以解决一下问题 :

1.查找元素属于哪个集合

沿着数组表示树形关系以上一直找到根(即:树中中元素为负数的位置)

2.查看两个元素是否属于同一个集合

沿着数组表示的树形关系往上一直找到树的根,如果根相同表明在同一个集合,否则不在

3.将两个集合归并成一个集合

(1)将两个集合中的元素合并

(2)将一个集合名称改成另一个集合的名称

4.集合的个数

遍历数组,数组中元素为负数的个数即为集合的个数。

原理学习算法学习笔记(1) : 并查集 - 知乎 (zhihu.com)
并查集常用模板:
class UnionFind{
    int[] parent;// parent[i]表示i这个元素指向的父亲节点
    int[] size;//size[i]表示以i为根节点的集合中元素个数
    int n;//节点的个数,初始化每一个节点都是一个单独的连通分量
    int setCount;//连通分量的数目
    public UnionFind(int n){
        this.size=new int[n];
        this.parent=new int[n];
        this.n=n;
        this.setCount=n;
        Arrays.fill(size,1);
        for(int i=0;i<n;i++){
            parent[i]=i;
        }
    }

    public int find(int x){
        return parent[x]==x?x:find(parent[x]);
    }

    public boolean unit(int x,int y){
        x=find(x);
        y=find(y);
        if(x==y){
            return false;
        }
        if(size[x]<size[y]){
            int tem=x;
            x=y;
            y=tem;
        }

        parent[y]=x;
        size[x]+=size[y];
        --setCount;
        return true;
    }

    public boolean connected(int x, int y) {
        x = find(x);
        y = find(y);
        return x == y;
    }

}

示例:

e.g.1

547. 省份数量 - 力扣(LeetCode)

n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。

省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。

给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。

返回矩阵中 省份 的数量。

示例 1:
输入:isConnected = [[1,1,0],[1,1,0],[0,0,1]] 输出:2
示例2:
输入:isConnected = [[1,0,0],[0,1,0],[0,0,1]] 输出:3
给定的矩阵 isConnected isConnected 即为图的邻接矩阵,省份即为图中的连通分量。
也可以dfs,bfs写
class Solution {
    public int findCircleNum(int[][] isConnected) {
        UnionFind uf = new UnionFind(isConnected);
        int len1 = isConnected.length;
        int len2 = isConnected[0].length;
        for(int i = 0 ; i < len1 ; i++){
            for(int j = 0 ; j < len2 ; j++){
                if(isConnected[i][j] == 1){
                    uf.union(i,j);
                }
            }
        }
        return uf.sum;
    }
}

//并查集及其常用方法
class UnionFind{
    int sum;
    int[] size;
    int[] parent;
    
    //初始化并查集
    UnionFind (int[][] isConnected){
        int len = isConnected.length;
        this.sum = len;//初始化长度
        this.size =new int[len];
        Arrays.fill(size,1);//用1填满数组
        this.parent =new int[len];
        for(int i = 0 ;i<len;i++){
            parent[i] = i;
        }
    }

    //获得省的省会
    public int getHead(int x){
        while(x != parent[x]){
            x = parent[x];
        }
        return x;
    }

    //俩个城市合并
    public void union(int x,int y){
        int xHead = getHead(x);
        int yHead = getHead(y);
        if(xHead == yHead){
            //说明是同一个组合,不用联合
            return;
        }
        if(xHead>yHead){
            parent[yHead] = xHead;
            size[xHead] += size[yHead];
            sum--;
        }else{//xHead < yHead
            parent[xHead] = yHead;
            size[yHead] += size[xHead];
            sum--;
        }

    }
}

e.g.2

695. 岛屿的最大面积 - 力扣(LeetCode)

给你一个大小为 m x n 的二进制矩阵 grid

岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。

岛屿的面积是岛上值为 1 的单元格的数目。

计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0

示例 1:


输入:grid = [[0,0,1,0,0,0,0,1,0,0,0,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,1,1,0,1,0,0,0,0,0,0,0,0],[0,1,0,0,1,1,0,0,1,0,1,0,0],[0,1,0,0,1,1,0,0,1,1,1,0,0],[0,0,0,0,0,0,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,0,0,0,0,0,0,1,1,0,0,0,0]]
输出:6
解释:答案不应该是 11 ,因为岛屿只能包含水平或垂直这四个方向上的 1 。
示例 2:

输入:grid = [[0,0,0,0,0,0,0,0]]
输出:0
class Solution {
    public int maxAreaOfIsland(int[][] grid) {
        UnionFind uf =  new UnionFind(grid);
        int len1 = grid.length,len2 = grid[0].length;
        for(int i = 0 ; i < len1 ;i++){
            for(int j = 0 ; j < len2 ;j++){
                if(grid[i][j] == 0) continue;
                if(i-1 >=0 && grid[i-1][j] == 1){
                    uf.union(i*len2+j,(i-1)*len2+j);
                }
                if(j-1 >=0 && grid[i][j-1] == 1){
                    uf.union(i*len2+j,i*len2+j-1);
                }
                if(i+1 < len1 && grid[i+1][j] == 1){
                    uf.union(i*len2+j,(i+1)*len2+j);
                }
                if(j+1 < len2 && grid[i][j+1] == 1){
                    uf.union(i*len2+j,i*len2+j+1);
                }
            }
        }
        int max = 0;
        for(int i = 0 ; i < uf.sum ; i++){
            if(uf.size[i] > max){
                max = uf.size[i];
            }
        }
        return max;
    }
}

class UnionFind{
    int sum;
    int[] size;
    int[] parent;
    UnionFind(int[][] grid){
        int len1 = grid.length,len2 = grid[0].length;
        this.sum = len1*len2;
        this.size = new int[sum];
        this.parent = new int[sum];
        for(int i = 0 ; i < len1 ; i++){
            for(int j = 0 ; j < len2 ; j++){
                parent[i*len2+j] = i*len2+j;//每行有len2个数
                if(grid[i][j] == 1){
                    size[i*len2 + j] = 1;
                }else{
                    size[i*len2 + j] = 0;
                }
            }
        }
    }

    public int getHead(int x){
        //传进来的x即为i*len2 + j
        while(x != parent[x]){
            x = parent[x];
        }
        return x;
    }

    public void union(int x,int y){
        int xHead = getHead(x);
        int yHead = getHead(y);
        if(xHead == yHead) return;//俩个已经在同一座岛屿上、
        if(xHead > yHead){
            size[xHead] += size[yHead];
            parent[yHead] = xHead;
        }else{
            size[yHead] += size[xHead];
            parent[xHead] = yHead;
        }
    }
}

e.g.3

200. 岛屿数量 - 力扣(LeetCode)

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

示例 1:

输入:grid = [
["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]
]
输出:1
示例 2:

输入:grid = [
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
输出:3
class Solution {
    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0) {
            return 0;
        }
        UnionFind uf = new UnionFind(grid);
        int len1 = grid.length,len2 = grid[0].length;
        for(int i = 0 ; i < len1 ; i++){
            for(int j = 0; j < len2 ; j++){
                if(grid[i][j] == '0') {
                    continue;
                }
                grid[i][j] = '0';
                if(i-1 >=0 && grid[i-1][j] == '1'){
                    uf.union(i*len2+j,(i-1)*len2+j);
                }
                if(j-1 >=0 && grid[i][j-1] == '1'){
                    uf.union(i*len2+j,i*len2+j-1);
                }
                if(i+1 < len1 && grid[i+1][j] == '1'){
                    uf.union(i*len2+j,(i+1)*len2+j);
                }
                if(j+1 < len2 && grid[i][j+1] == '1'){
                    uf.union(i*len2+j,i*len2+j+1);
                }
            }
        }
        return uf.getCount();
        
    }
}

class UnionFind{
    int sum;
    int count;
    int[] size;
    int[] parent;

    UnionFind(char[][] grid){
        int len1 = grid.length,len2 = grid[0].length;
        count =0;
        this.sum = len1*len2;
        this.size = new int[sum];
        this.parent = new int[sum];
        for(int i = 0 ; i < len1 ; i++){
            for(int j = 0 ; j < len2 ; j++){
                size[i*len2 + j] = 0;
                if(grid[i][j] == '1'){
                    ++count;
                    parent[i*len2+j] = i*len2+j;
                    //每行有len2个数
                }
            }
        }
    }
    public int getCount(){
        return count;
    }

    public int getHead(int x){
        //传进来的x即为i*len2 + j
        while(x != parent[x]){
            x = parent[x];
        }
        return x;
    }

    public void union(int x,int y){
        int rootx = getHead(x);
        int rooty = getHead(y);
        if (rootx != rooty) {
            if (size[rootx] > size[rooty]) {
                parent[rooty] = rootx;
            } else if (size[rootx] < size[rooty]) {
                parent[rootx] = rooty;
            } else {
                parent[rooty] = rootx;
                size[rootx] += 1;
            }
            --count;
        }
    }
}

感觉并查集像是套模板一样,不太会用hhhh

猜你喜欢

转载自blog.csdn.net/m0_63061397/article/details/129603894