并查集练习 — 岛屿问题(解法二)

news2024/12/23 14:40:15

题目如岛屿问题解法一文章所介绍,这里不过多赘述,直接讲解第二种解法。

并查集解法

并查集解法的整体思路是,将二维数组中为‘1’的部分提取出来作为样本,再进行判断,如果左上方向有同样为‘1’的,则进行合并,最后看一共有几座岛屿。
不过并查集解法有一个问题,给定的二维数组中是char类型,Java中基本数据都行都会作为值进行传递,那么该怎么分辨二维数组中所有的’1’是不相同的呢?

代码
比较粗糙的做法是用一个类替代这个‘1’,声明一个和参数board等长的Dot类的二维数组,从头至尾遍历一次,将board中为1的位置在Dot数组中对应位置创建Dot对象。
将Dot作为并查集中的样本。利用对象的内存地址不同来区别出不同的1,遍历,进行union后,返回集合个数。
代码主流程中,在进行union时一共进行了三次的遍历。
第一次遍历:只遍历第0列,因为第0列左侧没有值,所以不用考虑左侧。
第二次遍历:只遍历第0行,因为第0行上面没有值,所以不用考虑上面。
第三次遍历:从第1行第1列遍历。只考虑左上方向有没有‘1’,有‘1’则进行合并即可,不用再考虑边界问题。
如果用1个for循环搞定这些,可以。不过常数时间更复杂。每次都要判断边界。

 public static class Node<V> {
        V val;

        public Node(V value) {
            this.val = value;
        }
    }

    public static class Dot {

    }

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

        public UnionFind1(List<V> lists) {
            nodes = new HashMap<>();
            parentMap = new HashMap<>();
            sizeMap = new HashMap<>();

            for (V val : lists) {
                Node node = new Node(val);
                nodes.put(val, node);
                parentMap.put(node, node);
                sizeMap.put(node, 1);
            }
        }

        public Node<V> findParent(Node<V> node) {
            Stack<Node> stack = new Stack<>();
            while (node != parentMap.get(node)) {
                node = parentMap.get(node);
                stack.push(node);
            }

            while (!stack.isEmpty()) {
                parentMap.put(stack.pop(), node);
            }
            return node;
        }

        public void union(V a, V b) {
            Node aNode = findParent(nodes.get(a));
            Node bNode = findParent(nodes.get(b));

            if (aNode != bNode) {
                int aSize = sizeMap.get(aNode);
                int bSize = sizeMap.get(bNode);

                Node big = aSize >= bSize ? aNode : bNode;
                Node small = big == aNode ? bNode : aNode;

                parentMap.put(small, big);
                sizeMap.put(big, aSize + bSize);
                sizeMap.remove(small);
            }
        }

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

    public static int numIslands2(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> unionFind1 = new UnionFind1<>(dotList);

        for (int i = 1; i < col; i++) {
            if (board[0][i - 1] == '1' && board[0][i] == '1') {
                unionFind1.union(dots[0][i - 1], dots[0][i]);
            }
        }

        for (int i = 1; i < row; i++) {
            if (board[i - 1][0] == '1' && board[i][0] == '1') {
                unionFind1.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') {
                        unionFind1.union(dots[i][j], dots[i][j - 1]);
                    }

                    if (board[i - 1][j] == '1') {
                        unionFind1.union(dots[i][j], dots[i - 1][j]);
                    }
                }
            }
        }
        return unionFind1.getSize();
    }

优化

上面的Dot类很粗糙,为了区别 '1’声明了一个类,很臃肿,所以还可以将上面的方法进行优化。
因为传进来的是一个二维数组,优化的过程是将二维数组拉伸,变成一个一维数组。
根据二维数组的列和行,算出新数组整体的长度,并放到新数组中。
比如说下图:3行4列的数组
在这里插入图片描述
新数组长度 row * col = 12,放入新数组的规则是: 当前行数 * 总列数 + 当前列数,第0行放入新数组中就是数组下标 0 ~ 3,第1行0列的1,放入新数组中就是 1 * 4 + 0 = 4,以此类推。
依然是将二维数组中‘1’部分当做是样本放到并查集中。相同就合并(在新数组中),最后再查看数组中集合个数即可。

代码

 public static int numIslands3(char[][] board) {
        int row = board.length;
        int col = board[0].length;
        UnionFind2 unionFind2 = new UnionFind2(board);

        for (int i = 1; i < row; i++) {
            if (board[i - 1][0] == '1' && board[i][0] == '1') {
                unionFind2.union(i - 1, 0, i, 0);
            }
        }

        for (int j = 1; j < col; j++) {
            if (board[0][j - 1] == '1' && board[0][j] == '1') {
                unionFind2.union(0, j - 1, 0, j);
            }
        }

        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') {
                        unionFind2.union(i, j, i, j - 1);
                    }
                    if (board[i - 1][j] == '1') {
                        unionFind2.union(i, j, i - 1, j);
                    }
                }
            }
        }
        return unionFind2.getSets();
    }
    
	public static class UnionFind2 {
	        int[] parent;
	        int[] size;
	        int[] help;
	        int col;
	        int sets;
	
	        public UnionFind2(char[][] board) {
	            int row = board.length;
	            this.col = board[0].length;
	            int len = row * col;
	            parent = new int[len];
	            help = new int[len];
	            size = new int[len];
	            sets = 0;
	
	            for (int i = 0; i < row; i++) {
	                for (int j = 0; j < col; j++) {
	                    if (board[i][j] == '1') {
	                        int index = index(i, j);
	                        parent[index] = index;
	                        size[index] = 1;
	                        sets++;
	                    }
	                }
	            }
	        }
	
	        public int index(int row, int currCol) {
	            return row * col + currCol;
	        }
	
	        // index -> 计算后在parent中的下标;
	        public int findParent(int index) {
	            int num = 0;
	            while (index != parent[index]) {
	                index = parent[index];
	                help[num++] = index;
	            }
	
	            for (int i = 0; i < num; i++) {
	                parent[help[i]] = index;
	            }
	            return index;
	        }
	
	        public void union(int r1, int c1, int r2, int c2) {
	            int i1 = index(r1, c1);
	            int i2 = index(r2, c2);
	            int f1 = findParent(i1);
	            int f2 = findParent(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 getSets() {
	            return sets;
	        }
	    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/841068.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

架构训练营学习笔记:6-1 微服务

序 这部分是了解的。传统企业使用soa较多。很多企业银行、电信对于Oracle 依赖大&#xff0c;强调稳定性。各个项目侧重外包&#xff0c;技术栈不统一。 soa 历史 这个之前电信的BOSS系统就是这种架构&#xff0c;不知道现在呢&#xff0c;核心计费系统billing是运行在tuxduo…

整理mongodb文档:collation

文章连接 整理mongodb文档:collation 看前提示 对于mongodb的collation。个人主要用的范围是在createcollection&#xff0c;以及find的时候用&#xff0c;所以本片介绍的时候也是这两个地方入手&#xff0c;对新手个人觉得理解概念就好。不要求强制性掌握&#xff0c;但是要…

jmeter工具测试和压测websocket协议【杭州多测师_王sir】

一、安装JDK配置好环境变量&#xff0c;安装好jmeter 二、下载WebSocketSampler发送请求用的&#xff0c;地址&#xff1a;https://bitbucket.org/pjtr/jmeter-websocket-samplers/downloads/?spma2c4g.11186623.2.15.363f211bH03KeI 下载解压后的jar包放到D:\JMeter\apache-j…

MyCat配置rule.xml、server.xml讲解

1. rule.xml分片规则配置文件 rule.xml中配置的主要就是拆分表的规则&#xff0c;rule.xml中主要包含两类标签 tableRule 和Function。 tableRule标签里面主要配置我们的分片规则&#xff0c;Function里面涉及的是分片规则里面所涉及的java类&#xff0c;都是在function中配置…

python中的装饰器的真正含义和用法

闭包&#xff1a; 闭包是python中的一个很实用的写法&#xff0c;可以使得用户在函数中调用该函数外的函数的变量&#xff0c;使得该变量常驻于内存中。 闭包函数&#xff1a; 输入是函数&#xff0c;输出也是一个函数。 装饰器的写法是python闭包的语法糖。 面试中经常面…

[C++] KY79 浮点数加法 北京大学复试上机题

题目链接 KY79 浮点数加法https://www.nowcoder.com/questionTerminal/ddec753f446e4ba4944e35378ba635c8 描述 求2个浮点数相加的和 题目中输入输出中出现浮点数都有如下的形式&#xff1a; P1P2...Pi.Q1Q2...Qj 对于整数部分&#xff0c;P1P2...Pi是一个非负整数 对于小数…

【前端】html

HTML标签&#xff08;上&#xff09; 目标&#xff1a; -能够说出标签的书写注意规范 -能够写出HTML骨架标签 -能够写出超链接标签 -能够写出图片标签并说出alt和title的区别 -能够说出相对路径的三种形式 目录&#xff1a; HTML语法规范HTML基本结构标签开发工具HTML常用标…

C语言案例 99乘法口诀-04

难度2复杂度2 题目&#xff1a;打印99乘法口诀 步骤一&#xff1a;定义程序目标 编写一个C程序&#xff0c;打印99乘法口诀。 步骤二&#xff1a;程序设计 整个程序分别为两个部分&#xff0c;第一部分是使用for循环打印的行数&#xff0c;第二部分是使用for循环控制打印的列…

基于Azure OpenAI Service 的知识库搭建实验⼿册

1.概要 介绍如何使⽤Azure OpenAI Service 的嵌⼊技术&#xff0c;创建知识库&#xff1b;以及创建必要的资源组和资源&#xff0c;包括 Form Recognizer 资源和 Azure 翻译器资源。在创建问答机器⼈服务时&#xff0c;需要使⽤已部署模型的 Azure OpenAI 资源、已存在的…

一键翻译,实现文件名简体中文转日语的便捷改名!

您是否曾经遇到过想要将文件名从简体中文翻译成日语&#xff0c;却苦于没有合适的工具&#xff1f;现在&#xff0c;让我们为您介绍一款便捷的解决方案——文件名简体中文转日语翻译工具&#xff01; 首先&#xff0c;第一步&#xff0c;我们需要打开文件批量改名&#xff0c;…

无涯教程-Perl - binmode函数

描述 此函数设置在区分两者的操作系统上以二进制形式读取和写入FILEHANDLE的格式。非二进制文件的CR LF序列在输入时转换为LF,在LF时在输出时转换为CR LF。这对于使用两个字符分隔文本文件中的行的操作系统(MS-DOS)至关重要,但对使用单个字符的操作系统(Unix,Mac OS,QNX)没有影…

付款方式谈得好,订单跑不了

在与客户商讨付款方式时出现分歧&#xff0c;其实是非常常见的情况。 在这种情况下&#xff0c;我们需要紧握谈判的节奏&#xff0c;不可忽视任何细节&#xff0c;更不能让自己陷入混乱之中。 先看一个案例~ 客户发来邮件&#xff1a; Dear... Please find our attached f…

远程访问桌面软件 OpenText Exceed TurboX(ETX)如何提高企业生产力

远程访问桌面软件 OpenText Exceed TurboX&#xff08;ETX&#xff09;如何提高企业生产力 几乎所有规模和行业的企业&#xff0c;员工的工作方式、时间和地点方面发生重大变化&#xff0c;这主要得益于新技术和全球商业与协作。业务领导者正在推动其 IT 部门提出解决方案&…

使用yarn启动项目报错

使用yarn启动项目报错 解决方法&#xff1a; 1.点击“开始”菜单搜索找到 Windows PowerShell ISE并以管理员身份运行(注&#xff1a;不是以管理员的身份直接运行cmd) 2. 输入 set-ExecutionPolicy RemoteSigned 回车 3.输入&#xff08;选择全是&#xff09; 4.再输入get-Exe…

【Linux】五、进程

一、冯诺依曼体系结构 存储器&#xff1a;指的是内存&#xff1b; 输入设备&#xff1a;键盘、摄像头、话筒&#xff0c;磁盘&#xff0c;网卡&#xff1b; 输出设备&#xff1a;显示器、音响、磁盘、网卡&#xff1b; 中央处理器&#xff08;CPU&#xff09;&#xff1a;运算器…

【架构】Java 系统架构演进的思考

文章目录 1 前言2 单体应用架构3 垂直应用架构4 分布式架构5 SOA 架构6 微服务云架构7 总结 1 前言 随着移动互联的发展&#xff0c;网站、H5、移动端的应用规模也不断扩大&#xff0c;不管是应用的数量还是质量都得到了指数级的提升。开发者的数量与日俱增&#xff0c;应用的…

Python实现GA遗传算法优化Catboost回归模型(CatBoostRegressor算法)项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档视频讲解&#xff09;&#xff0c;如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 遗传算法&#xff08;Genetic Algorithm&#xff0c;GA&#xff09;最早是由美国的 John holland于20世…

The ‘kotlin-android-extensions‘ Gradle plugin is no longer supported.

Android使用kotlin开发&#xff0c;运行报错 The kotlin-android-extensions Gradle plugin is no longer supported. Please use this migration guide (https://goo.gle/kotlin-android-extensions-deprecation) to start working with View Binding (https://developer.an…

FPGA优质开源项目 - UDP RGMII千兆以太网

本文介绍一个FPGA开源项目&#xff1a;UDP RGMII千兆以太网通信。该项目在我之前的工作中主要是用于FPGA和电脑端之间进行图像数据传输。本文简要介绍一下该项目的千兆以太网通信方案、以太网IP核的使用以及Vivado工程源代码结构。 Vivado 的 Tri Mode Ethernet MAC IP核需要付…

HTTP——九、基于HTTP的功能追加协议

HTTP 一、基于HTTP的协议二、消除HTTP瓶颈的SPDY1、HTTP的瓶颈Ajax 的解决方法Comet 的解决方法SPDY的目标 2、SPDY的设计与功能3、SPDY消除 Web 瓶颈了吗 三、使用浏览器进行全双工通信的WebSocket1、WebSocket 的设计与功能2、WebSocket协议 四、期盼已久的 HTTP/2.01、HTTP/…