并查集模板Java

本文介绍了并查集算法及其在解决连通性问题中的应用,如岛屿数量计算。并查集主要包含初始化、查询和合并三个步骤。通过Java实现并查集模板类,详细解析了岛屿数量问题的解题思路,即遍历矩阵,用并查集合并相邻的1,最终返回岛屿数量。
摘要由CSDN通过智能技术生成

介绍

并查集算法一般用于解决最小生成树问题,连通性问题,寻找子树问题,判断一个集合里是否存在子集。
部分能用并查集解决的一般都可以用DFS,BFS解决,并查集思想也与这二者类似

并查集的大致过程:不断构建子树并进行合并的过程

并查集三步曲

  1. 初始化,将每个元素的祖先设置为自己
int fa[MAXN]
  1. 查询
    找到i的祖先直接返回,未进行路径压缩
int find(int i){
if(fa[i]==i){
return i;
 }else{
return find(fa[i])
}

路径压缩版:返回时,将出口返回的父亲,全部设置为递归路径所经过节点的祖先

int find(int i){
if(fa[i]==i){
return i;
 }else{
    fa[i]=find(fa[i])
return fa[i];
}
  1. 合并,将祖先合并
    注意:
    if(i_fa!=j_fa){
    fa[i_fa]=j_fa;//将i的祖先指向j的祖先
    }
    注意这里是为了避免重复合并
void union(int i,int j){
int i_fa=find(i);
int j_fa=find(j);
if(i_fa!=j_fa){
fa[i_fa]=j_fa;//将i的祖先指向j的祖先
}
}

一开始每个元素都指向自己,也就相当于初始化操作
在这里插入图片描述
两两互相合并后的集合,可以与不同的其他集合合并
在这里插入图片描述

java定义一个类做为并查集模板

class UnionFind{
    private int[]father;//创建祖先数组,并将每个元素的祖先设置为自己

    private int count=0;
    private int []father;
    public int getCount(){
        this.count=count;
        return count;
    }


    //查寻各自的祖先
    public int find(int i){
        if (father[i]==i){
            return i;
        }
        father[i]=find(father[i]);//???
        return father[i];
    }
    //集(合并操作)
   public void union(int i,int j){
        int ifather=find(i);
        int jfather=find(j);
        if (ifather!=jfather){//避免重复和并
            father[ifather]=jfather;
            count++;//合并次数
        }

    }

}

例题

岛屿数量
给你一个由 ‘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

思路

观察到连通的一片1就可以视为一座岛屿,使用并查集,遍历岛屿以其中一个1作为中心,将四周为1的元素合并,并记录合并次数
岛屿的数量=总元素-水域-合并的次数

代码

package practise.第十二届模拟赛;

import java.util.Scanner;

public class 岛屿数量 {
    private static int water=0;
    private static int unionnnum;

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int n=scanner.nextInt();
        int m=scanner.nextInt();//n行m列的岛屿
        int[][] grid = new int[n][m];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                grid[i][j]=scanner.nextInt();
            }
        }
        int num=thenumofland(grid);
        System.out.println(num);

    }

   public static int thenumofland(int[][] grid) {
       if (grid == null || grid.length == 0) {
           return 0;
       }
        int row=grid.length;
        int low=grid[0].length;
        UnionFind unionFind = new UnionFind(grid);
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < low; j++) {
           if (grid[i][j]==0){
               water++;
           }else {
               int[][]dir ={{0,1},{1,0},{-1,0},{0,-1}};
               for (int[]direction:dir) {//将dir中的元素,依次赋给direction并更新
                   int x=i+direction[0];
                   int y=j+direction[1];
                   if (x>=0&&y>=0&&x<row&&y<low&&grid[x][y]==1){//边界条件出错
                       unionFind.union(low*i+j,low*x+y);//和并情况判断出错
                   }
               }
           }
            }
        }
        return row*low-water-unionFind.getCount();

    }
}
class UnionFind{
    private int[]father;//创建祖先数组,并将每个元素的祖先设置为自己

    private int count=0;
    public int getCount(){
        this.count=count;
        return count;
    }

    public UnionFind(int [][] grid){//构造器,将二维数组对应数改为father中元素
        father=new int[grid.length* grid[0].length];
        for (int i=0;i<grid.length* grid[0].length;i++){
            father[i]=i;//初始化
        }
    }

    //查寻各自的祖先
    public int find(int i){
        if (father[i]==i){
            return i;
        }
        father[i]=find(father[i]);
        return father[i];
    }
    //集(合并操作)
   public void union(int i,int j){
        int ifather=find(i);
        int jfather=find(j);
        if (ifather!=jfather){//避免重复和并
            father[ifather]=jfather;
            count++;
        }

    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值