并查集练习——岛屿数量

简介: 并查集练习——岛屿数量

力扣原题链接:岛屿数量

给你一个由 '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

提示:

m == grid.length
n == grid[i].length
1 <= m, n <= 300
gridi 的值为 '0' 或 '1'

package com.harrison.class10;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Stack;

/**
 * @author Harrison
 * @create 2022-03-19-16:46
 * @motto 众里寻他千百度,蓦然回首,那人却在灯火阑珊处。
 */
public class Code03_NumberOfIslands {
    // https://leetcode.com/problems/number-of-islands/
    public static int numIslands3(char[][] board){
        int islands=0;
        for(int i=0; i<board.length; i++){
            for(int j=0; j<board[0].length; j++){
                if(board[i][j]=='1'){
                    islands++;
                    infect(board,i,j);
                }
            }
        }
        return islands;
    }

    public static void infect(char[][] board,int i,int j){
        if(i<0 || i==board.length || j<0 || j==board[0].length || board[i][j]!='1'){
            return ;
        }
        board[i][j]=2;
        infect(board,i-1,j);
        infect(board,i+1,j);
        infect(board,i,j-1);
        infect(board,i,j+1);
    }

    public static int numIslands1(char[][] board){
        int row=board.length;
        int col=board[0].length;
        Dot[][] dots=new Dot[row][col];
        List<Dot> dotList=new ArrayList<>();
        for(int i=0; i<row; i++){
            for(int j=0; j<col; j++){
                if(board[i][j]=='1'){
                    dots[i][j]=new Dot();
                    dotList.add(dots[i][j]);
                }
            }
        }
        UnionFind1<Dot> uf=new UnionFind1<>(dotList);
        for(int j=1; j<col; j++){
            if(board[0][j-1]=='1' && board[0][j]=='1'){
                uf.union(dots[0][j-1],dots[0][j]);
            }
        }
        for(int i=1; i<row; i++){
            if(board[i-1][0]=='1' && board[i][0]=='1'){
                uf.union(dots[i-1][0],dots[i][0]);
            }
        }
        for(int i=1; i<row; i++){
            for(int j=1; j<col; j++){
                if(board[i][j]=='1'){
                    if(board[i][j-1]=='1'){
                        uf.union(dots[i][j-1],dots[i][j]);
                    }
                    if(board[i-1][j]=='1'){
                        uf.union(dots[i-1][j],dots[i][j]);
                    }
                }

            }
        }
        return uf.sets();
    }

    public static class Dot{

    }

    public static class Node<V>{
        public V value;

        public Node(V v){
            value=v;
        }
    }

    public static class UnionFind1<V>{
        public HashMap<V,Node<V>> nodes;
        public HashMap<Node<V>,Node<V>> parent;
        public HashMap<Node<V>,Integer> sizeMap;

        public UnionFind1(List<V> values){
            nodes=new HashMap<>();
            parent=new HashMap<>();
            sizeMap=new HashMap<>();
            for(V cur:values){
                Node<V> node=new Node<>(cur);
                nodes.put(cur,node);
                parent.put(node,node);
                sizeMap.put(node,1);
            }
        }

        public Node<V> findAncestor(Node<V> cur){
            Stack<Node<V>> path=new Stack<>();
            while (cur!=parent.get(cur)){
                path.push(cur);
                cur=parent.get(cur);
            }
            while (!path.isEmpty()){
                parent.put(path.pop(),cur);
            }
            return cur;
        }

        public void union(V a,V b){
            Node<V> aHead=findAncestor(nodes.get(a));
            Node<V> bHead=findAncestor(nodes.get(b));
            if(aHead!=bHead){
                int aSetSize=sizeMap.get(aHead);
                int bSetSize=sizeMap.get(bHead);
                Node<V> big=aSetSize>=bSetSize?aHead:bHead;
                Node<V> small=aHead==big?bHead:aHead;
                parent.put(small,big);
                sizeMap.put(big,aSetSize+bSetSize);
                sizeMap.remove(small);
            }
        }

        public int sets(){
            return sizeMap.size();
        }
    }

    public static int numIslands2(char[][] board){
        int row=board.length;
        int col=board[0].length;
        UnionFind2 uf=new UnionFind2(board);
        for(int j=1; j<col; j++){
            if(board[0][j-1]=='1' && board[0][j]=='1'){
                uf.union(0,j-1,0,j);
            }
        }
        for(int i=1; i<row; i++){
            if(board[i-1][0]=='1' && board[i][0]=='1'){
                uf.union(i-1,0,i,0);
            }
        }
        for(int i=1; i<row; i++){
            for(int j=1; j<col; j++){
                if(board[i][j]=='1'){
                    if(board[i][j-1]=='1'){
                        uf.union(i,j-1,i,j);
                    }
                    if(board[i-1][j]=='1'){
                        uf.union(i-1,j,i,j);
                    }
                }
            }
        }
        return uf.sets;
    }

    public static class UnionFind2{
        public int[] parent;
        public int[] size;
        public int[] help;
        public int col;
        public int sets;

        public UnionFind2(char[][] board){
            int row=board.length;
            col=board[0].length;
            sets=0;
            int len=row*col;
            parent=new int[len];
            size=new int[len];
            help=new int[len];
            for(int r=0; r<row; r++){
                for(int c=0; c<col; c++){
                    if(board[r][c]=='1'){
                        int i=index(r,c);
                        parent[i]=i;
                        size[i]=1;
                        sets++;
                    }
                }
            }
        }

        // (i,j) -> i*列数+j
        public int index(int r,int c) {
            return r*col+c;
        }

        // 原始位置 -> 下标
        public int find(int i){
            int hi=0;
            while (i!=parent[i]){
                help[hi++]=i;
                i=parent[i];
            }
            for(hi--; hi>=0; hi--){
                parent[help[hi]]=i;
            }
            return i;
        }

        public void union(int r1,int c1,int r2,int c2){
            int i1=index(r1,c1);
            int i2=index(r2,c2);
            int f1=find(i1);
            int f2=find(i2);
            if(f1!=f2){
                if(size[f1]>=size[f2]){
                    size[f1]+=size[f2];
                    parent[f2]=f1;
                }else{
                    size[f2]=size[f1];
                    parent[f1]=f2;
                }
                sets--;
            }
        }

        public int sets(){
            return sets;
        }
    }

    // 为了测试
    public static char[][] generateRandomMatrix(int row, int col) {
        char[][] board = new char[row][col];
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                board[i][j] = Math.random() < 0.5 ? '1' : '0';
            }
        }
        return board;
    }

    // 为了测试
    public static char[][] copy(char[][] board) {
        int row = board.length;
        int col = board[0].length;
        char[][] ans = new char[row][col];
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                ans[i][j] = board[i][j];
            }
        }
        return ans;
    }

    // 为了测试
    public static void main(String[] args) {
        int row = 0;
        int col = 0;
        char[][] board1 = null;
        char[][] board2 = null;
        char[][] board3 = null;
        long start = 0;
        long end = 0;

        row = 1000;
        col = 1000;
        board1 = generateRandomMatrix(row, col);
        board2 = copy(board1);
        board3 = copy(board1);

        System.out.println("感染方法、并查集(map实现)、并查集(数组实现)的运行结果和运行时间");
        System.out.println("随机生成的二维矩阵规模 : " + row + " * " + col);

        start = System.currentTimeMillis();
        System.out.println("感染方法的运行结果: " + numIslands3(board1));
        end = System.currentTimeMillis();
        System.out.println("感染方法的运行时间: " + (end - start) + " ms");

        start = System.currentTimeMillis();
        System.out.println("并查集(map实现)的运行结果: " + numIslands1(board2));
        end = System.currentTimeMillis();
        System.out.println("并查集(map实现)的运行时间: " + (end - start) + " ms");

        start = System.currentTimeMillis();
        System.out.println("并查集(数组实现)的运行结果: " + numIslands2(board3));
        end = System.currentTimeMillis();
        System.out.println("并查集(数组实现)的运行时间: " + (end - start) + " ms");

        System.out.println();

        row = 10000;
        col = 10000;
        board1 = generateRandomMatrix(row, col);
        board3 = copy(board1);
        System.out.println("感染方法、并查集(数组实现)的运行结果和运行时间");
        System.out.println("随机生成的二维矩阵规模 : " + row + " * " + col);

        start = System.currentTimeMillis();
        System.out.println("感染方法的运行结果: " + numIslands3(board1));
        end = System.currentTimeMillis();
        System.out.println("感染方法的运行时间: " + (end - start) + " ms");

        start = System.currentTimeMillis();
        System.out.println("并查集(数组实现)的运行结果: " + numIslands2(board3));
        end = System.currentTimeMillis();
        System.out.println("并查集(数组实现)的运行时间: " + (end - start) + " ms");

    }
}

在这里插入图片描述

相关文章
|
8月前
|
人工智能 自然语言处理 数据处理
还在手动验证文献引用?ScholarCopilot:开源AI学术写作工具,生成时实时插入文献引用
基于 Qwen-2.5-7B 模型的 ScholarCopilot 通过动态检索标记和联合优化技术,实现学术文本生成与文献引用的精准匹配,在 50 万篇论文库中实现 40.1% 的检索准确率,生成文本的学术严谨性评分达 16.2/25。
1089 5
还在手动验证文献引用?ScholarCopilot:开源AI学术写作工具,生成时实时插入文献引用
|
域名解析 网络协议 安全
信息收集的工具你听过几种(盘点信息收集)
信息收集的工具你听过几种(盘点信息收集)
信息收集的工具你听过几种(盘点信息收集)
|
消息中间件 人工智能 Apache
Apache RocketMQ 中文社区全新升级!
RocketMQ 中文社区升级发布只是起点,我们将持续优化体验细节,推出更多功能和服务,更重要的是提供更多全面、深度、高质量的内容。
975 97
|
9月前
|
设计模式 存储 JavaScript
HarmonyOS Next 浅谈 发布-订阅模式
本文浅谈 HarmonyOS Next 中的发布-订阅模式,通过 ArkTS 的 Emitter 对象实现事件的订阅、发布与管理。文章介绍了设计模式在现代开发中的重要性,特别是封装工具或游戏开发时的应用。具体实现中定义了 `on`(持续订阅)、`once`(单次订阅)、`off`(取消订阅)和 `emit`(发布事件)等方法,并通过 TypeScript 实现了一个自定义的 `MyEmitter` 类。示例代码展示了如何注册、触发和取消事件,图文并茂地说明了该模式的实际效果。发布-订阅模式有助于系统解耦,提升代码的可扩展性和灵活性,适合需要高效事件管理的场景。
237 12
HarmonyOS Next 浅谈 发布-订阅模式
|
9月前
|
人工智能 自然语言处理
TxGemma:谷歌DeepMind革命药物研发!270亿参数AI药理学家24小时在线
谷歌推出专为药物研发设计的TxGemma大模型,具备药物特性预测、生物文献筛选、多步推理等核心能力,提供20亿至270亿参数版本,显著提升治疗开发效率。
322 7
TxGemma:谷歌DeepMind革命药物研发!270亿参数AI药理学家24小时在线
|
监控 JavaScript API
局域网监控软件的实时通知系统:利用Node.js和WebSocket实现即时消息推送
本文介绍了如何使用Node.js和WebSocket构建局域网监控软件的实时通知系统。实时通知对于网络安全和家庭监控至关重要,能即时发送监控数据变化的通知,提高响应速度。通过Node.js创建WebSocket服务器,当数据变化时,监控软件发送消息至服务器,服务器随即推送给客户端。此外,还展示了如何利用Node.js编写API,自动将监控数据提交到网站,便于用户查看历史记录,从而提升监控体验。
523 3
|
Dart
Flutter|常用数据通信组件
在做需求时经常会遇到组件间通信,本篇汇总了几种常用的通信方式
333 57
|
机器学习/深度学习 并行计算 TensorFlow
GPU加速TensorFlow模型训练:从环境配置到代码实践的全方位指南,助你大幅提升深度学习应用性能,让模型训练不再等待
【8月更文挑战第31天】本文以随笔形式探讨了如何在TensorFlow中利用GPU加速模型训练,并提供了详细的实践指南。从安装支持GPU的TensorFlow版本到配置NVIDIA CUDA及cuDNN库,再到构建CNN模型并使用MNIST数据集训练,全面展示了GPU加速的重要性与实现方法。通过对比CPU与GPU上的训练效果,突显了GPU在提升训练速度方面的显著优势。最后,还介绍了如何借助TensorBoard监控训练过程,以便进一步优化模型。
2280 0
|
Java Python
Python 生成、解析二维码
Python 生成、解析二维码
334 0
|
人工智能 自然语言处理 测试技术
这些VLM竟都是盲人?GPT-4o、Sonnet-3.5相继败于视力测试
【7月更文挑战第28天】新研究表明VLM在简单视觉任务上的局限性。论文《Vision language models are blind》指出, GPT-4o、Claude-3.5 Sonnet等顶级模型在如判断形状重叠或字母识别等基本任务上表现不佳。另一研究在CVPR&#39;24上介绍了一个新框架, 利用TRUMANS数据集生成精细的人物动作, 包括手部运动, 显示出在复杂场景下的强大能力, 尽管仍面临一定的局限。[论文链接](https://arxiv.org/pdf/2407.06581) [TRUMANS](https://arxiv.org/pdf/2403.08629)
311 4