代码随想录算法训练Day58|LeetCode417-太平洋大西洋水流问题、LeetCode827-最大人工岛

news2024/11/26 18:49:00

太平洋大西洋水流问题

力扣417-太平洋大西洋水流问题

有一个 m × n 的矩形岛屿,与 太平洋 和 大西洋 相邻。 “太平洋” 处于大陆的左边界和上边界,而 “大西洋” 处于大陆的右边界和下边界。

这个岛被分割成一个由若干方形单元格组成的网格。给定一个 m x n 的整数矩阵 heights , heights[r] [c] 表示坐标 (r, c) 上单元格 高于海平面的高度

岛上雨水较多,如果相邻单元格的高度 小于或等于 当前单元格的高度,雨水可以直接向北、南、东、西流向相邻单元格。水可以从海洋附近的任何单元格流入海洋。

返回网格坐标 result 的 2D 列表 ,其中 result[i] = [ri, ci] 表示雨水从单元格 (ri, ci) 流动既可流向太平洋也可流向大西洋 。

示例 1:

img

  • 输入: heights = [[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]]
  • 输出: [[0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0]]

示例 2:

  • 输入: heights = [[2,1],[1,2]]
  • 输出: [[0,0],[0,1],[1,0],[1,1]]

解题思路

DFS 通过递归或栈来实现,沿着一个路径尽可能深地搜索,直到到达最远的节点,然后回溯并探索其他路径。在这个问题中,从每个起始点开始,DFS 沿着四个方向探索,直到无法继续前进或达到边界。

class Solution {
    private final int[][] dir = {{-1, 0}, {0, -1}, {1, 0}, {0, 1}}; // 保存四个方向

    // 从低向高遍历,注意这里 visited 是引用,即可以改变传入的 pacific 和 atlantic 的值
    private void dfs(int[][] heights, boolean[][] visited, int x, int y) {
        if (visited[x][y]) return;//如果当前坐标 (x, y) 已经被访问过,则直接返回,不再进行后续操作。
        visited[x][y] = true;//只要加入,立刻标记为访问过的节点
        for (int[] d : dir) { // 向四个方向遍历 8-17
            int nextx = x + d[0];
            int nexty = y + d[1];
            // 超过边界
            if (nextx < 0 || nextx >= heights.length || nexty < 0 || nexty >= heights[0].length) continue;
            // 高度不合适,注意这里是从低向高判断
            if (heights[x][y] > heights[nextx][nexty]) continue;

            dfs(heights, visited, nextx, nexty);
        }
    }

    public List<List<Integer>> pacificAtlantic(int[][] heights) {
        List<List<Integer>> result = new ArrayList<>();
        int n = heights.length;
        int m = heights[0].length; // 这里不用担心空指针,题目要求说了长宽都大于1

        // 记录从太平洋边出发,可以遍历的节点
        boolean[][] pacific = new boolean[n][m];

        // 记录从大西洋出发,可以遍历的节点
        boolean[][] atlantic = new boolean[n][m];

        // 从最上最下行的节点出发,向高处遍历
        for (int i = 0; i < n; i++) {
            dfs(heights, pacific, i, 0); // 遍历最左列,接触太平洋 
            dfs(heights, atlantic, i, m - 1); // 遍历最右列,接触大西洋 
        }

        // 从最左最右列的节点出发,向高处遍历
        for (int j = 0; j < m; j++) {
            dfs(heights, pacific, 0, j); // 遍历最上行,接触太平洋
            dfs(heights, atlantic, n - 1, j); // 遍历最下行,接触大西洋
        }

        // 找到同时被太平洋和大西洋访问的节点
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (pacific[i][j] && atlantic[i][j]) result.add(List.of(i, j));
            }
        }
        return result;
    }
}

result.add(List.of(i, j)

List.of()
List.of方法允许我们创建一个不可变的List集合,其中包含指定的元素。
List immutableList = List.of(“apple”, “banana”, “orange”);

Map.of()
Map.of方法允许我们创建一个不可变的Map集合,其中包含指定的键值对。
Map<String, Integer> immutableMap = Map.of(“apple”, 1, “banana”, 2, “orange”, 3);

Set.of()
Set.of方法允许我们创建一个不可变的Set集合,其中包含指定的元素。
Set immutableSet = Set.of(“apple”, “banana”, “orange”);

最大人工岛

题目描述

力扣827-最大人工岛

给你一个大小为 n x n 二进制矩阵 grid 。最多 只能将一格 0 变成 1 。

返回执行此操作后,grid 中最大的岛屿面积是多少?

岛屿 由一组上、下、左、右四个方向相连的 1 形成。

示例 1:

  • 输入: grid = [[1, 0], [0, 1]]
  • 输出: 3
  • 解释: 将一格0变成1,最终连通两个小岛得到面积为 3 的岛屿。

示例 2:

  • 输入: grid = [[1, 1], [1, 0]]
  • 输出: 4
  • 解释: 将一格0变成1,岛屿的面积扩大为 4。

示例 3:

  • 输入: grid = [[1, 1], [1, 1]]
  • 输出: 4
  • 解释: 没有0可以让我们变成1,面积依然为 4。

解题思路

其实每次深搜遍历计算最大岛屿面积,我们都做了很多重复的工作。

只要用一次深搜把每个岛屿的面积记录下来就好。

第一步:一次遍历地图,得出各个岛屿的面积,并做编号记录。可以使用map记录,key为岛屿编号,value为岛屿面积 第二步:在遍历地图,遍历0的方格(因为要将0变成1),并统计该1(由0变成的1)周边岛屿面积,将其相邻面积相加在一起,遍历所有 0 之后,就可以得出 选一个0变成1 之后的最大面积。

拿如下地图的岛屿情况来举例: (1为陆地)

img

第一步,则遍历题目,并将岛屿到编号和面积上的统计,过程如图所示:

img

本过程代码如下:

    private int[][] dir = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}}; // 四个方向

    private void dfs(int[][] grid, boolean[][] visited, int x, int y, int mark) {
        if (visited[x][y] || grid[x][y] == 0) return; // 终止条件:访问过的节点 或者 遇到海水
        visited[x][y] = true; // 标记访问过
        grid[x][y] = mark; // 给陆地标记新标签
        count++;
        for (int i = 0; i < 4; i++) {
            int nextx = x + dir[i][0];
            int nexty = y + dir[i][1];
            if (nextx < 0 || nextx >= grid.length || nexty < 0 || nexty >= grid[0].length) continue;  // 越界了,直接跳过
            dfs(grid, visited, nextx, nexty, mark);
        }
    }

    public int largestIsland(int[][] grid) {
        int n = grid.length, m = grid[0].length;
        boolean[][] visited = new boolean[n][m]; // 标记访问过的点
        Map<Integer, Integer> gridNum = new HashMap<>();
        int mark = 2; // 记录每个岛屿的编号
        boolean isAllGrid = true; // 标记是否整个地图都是陆地
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (grid[i][j] == 0) isAllGrid = false;
                if (!visited[i][j] && grid[i][j] == 1) {
                    count = 0;
                    dfs(grid, visited, i, j, mark); // 将与其链接的陆地都标记上 true
                    gridNum.put(mark, count); // 记录每一个岛屿的面积
                    mark++; // 记录下一个岛屿编号
                }
            }
        }
    }

这个过程时间复杂度 n * n 。可能有录友想:分明是两个for循环下面套这一个dfs,时间复杂度怎么回事 n * n呢?

其实大家可以仔细看一下代码,n * n这个方格地图中,每个节点我们就遍历一次,并不会重复遍历

第二步过程如图所示:

img

也就是遍历每一个0的方格,并统计其相邻岛屿面积,最后取一个最大值。

这个过程的时间复杂度也为 n * n。

所以整个解法的时间复杂度,为 n * n + n * n 也就是 n^2。

当然这里还有一个优化的点,就是 可以不用 visited数组,因为有mark来标记,所以遍历过的grid[i] [j]是不等于1的。

不过为了让各个变量各司其事,代码清晰一些,完整代码还是使用visited数组来标记。

最后,整体代码如下:

class Solution {
    private int count;
    private int[][] dir = { { 0, 1 }, { 1, 0 }, { -1, 0 }, { 0, -1 } }; // 四个方向

    private void dfs(int[][] grid, boolean[][] visited, int x, int y, int mark) {
        if (visited[x][y] || grid[x][y] == 0)
            return; // 终止条件:访问过的节点 或者 遇到海水
        visited[x][y] = true; // 标记访问过
        grid[x][y] = mark; // 给陆地标记新标签
        count++;
        for (int i = 0; i < 4; i++) {
            int nextx = x + dir[i][0];
            int nexty = y + dir[i][1];
            if (nextx < 0 || nextx >= grid.length || nexty < 0 || nexty >= grid[0].length)
                continue; // 越界了,直接跳过
            dfs(grid, visited, nextx, nexty, mark);
        }
    }

    public int largestIsland(int[][] grid) {
        int n = grid.length, m = grid[0].length;
        boolean[][] visited = new boolean[n][m]; // 标记访问过的点
        Map<Integer, Integer> gridNum = new HashMap<>();
        int mark = 2; // 记录每个岛屿的编号
        boolean isAllGrid = true; // 标记是否整个地图都是陆地
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (grid[i][j] == 0)
                    isAllGrid = false;
                if (!visited[i][j] && grid[i][j] == 1) {
                    count = 0;
                    dfs(grid, visited, i, j, mark); // 将与其链接的陆地都标记上 true
                    gridNum.put(mark, count); // 记录每一个岛屿的面积
                    mark++; // 记录下一个岛屿编号
                }
            }
        }
        if (isAllGrid)
            return n * m; // 如果都是陆地,返回全面积

        // 以下逻辑是根据添加陆地的位置,计算周边岛屿面积之和
        int result = 0; // 记录最后结果
        Set<Integer> visitedGrid = new HashSet<>(); // 标记访问过的岛屿
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                int currentCount = 1; // 记录连接之后的岛屿数量
                visitedGrid.clear(); // 每次使用时,清空
                if (grid[i][j] == 0) {
                    for (int k = 0; k < 4; k++) {
                        int neari = i + dir[k][0]; // 计算相邻坐标
                        int nearj = j + dir[k][1];
                        if (neari < 0 || neari >= grid.length || nearj < 0 || nearj >= grid[0].length)
                            continue;
                        if (visitedGrid.contains(grid[neari][nearj]))
                            continue; // 添加过的岛屿不要重复添加
                        // 把相邻四面的岛屿数量加起来
                        if (grid[neari][nearj] > 1) {
                            currentCount += gridNum.get(grid[neari][nearj]);
                            visitedGrid.add(grid[neari][nearj]); // 标记该岛屿已经添加过
                        }
                    }
                }
                result = Math.max(result, currentCount);
            }
        }
        return result;
    }
}

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

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

相关文章

SQL 对一个经常有数据更新和删除操作的表,怎样优化以减少磁盘空间的占用?

文章目录 一、定期清理不再需要的数据二、使用合适的数据类型三、压缩数据四、删除重复数据五、分区表六、索引优化七、碎片整理八、归档历史数据九、监控和评估 在数据库管理中&#xff0c;当面对一个经常进行数据更新和删除操作的表时&#xff0c;磁盘空间的有效利用是一个重…

【国产开源可视化引擎Meta2d.js】钢笔

钢笔 钢笔是和其他众多绘图工具&#xff08;Photoshop、Sketch、Illustrator&#xff09;中一致的钢笔工具&#xff0c;能够很方便的在线绘制各种小图标 在线体验&#xff1a; 乐吾乐2D可视化 示例&#xff1a; // 开始绘画&#xff1a;curve。除了curve&#xff0c;还有poly…

【HTML入门】第三课 - 标题、段落、空格

这一小节&#xff0c;我们说一些比较零散的知识&#xff0c;HTML课程中呢&#xff0c;其实就是一些标签&#xff0c;正是这些标签组成了前端网页的各种元素&#xff0c;所以你也可以叫他们标签元素。 像前两节我们说的&#xff0c;html head body title meta style 。这些都是…

【Unity性能消耗】ScriptableObject复用数据节省内存占用

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 专栏交流&#x1f9e7;&…

【电商系统开发实用接口指南】包含国内国外多电商平台商品数据对接(附文档)

关于电商数据接口 开发电商系统的朋友对于电商平台API肯定不陌生&#xff0c;API接口即应用程序编程接口&#xff0c;电商平台开放部分API接口&#xff0c;供商家和服务商调用&#xff0c;以满足电商业务管理需求。随着电商市场需求的日益增长以及技术手段的不断成熟&#xf…

深度学习与CV入门

文章目录 前言历史 前言 历史 tensorflow可以安装Tensorboard第三方库用于展示效果 TensorFlow工作流程&#xff1a;p6-4:20 使用tf.data加载数据。使用tf.data实例化读取训练数据和测试数据模型的建立与调试:使用动态图模式Eager Execution和著名的神经网络高层API框架Ker…

【Python】已解决:ModuleNotFoundError: No module named ‘nltk’

文章目录 一、分析问题背景二、可能出错的原因三、错误代码示例四、正确代码示例五、注意事项 已解决&#xff1a;ModuleNotFoundError: No module named ‘nltk’ 一、分析问题背景 在使用Python进行自然语言处理或文本分析时&#xff0c;我们经常会用到各种库来辅助我们的工…

旅游计划定制小程序网页模板源码

手机在线旅游定制服务&#xff0c;定制旅游出行app小程序模板。包含&#xff1a;定制介绍、定制表单填写、我的订单等。 旅游计划定制小程序网页模板源码

docker部署redis/mongodb/

一、redis 创建/root/redis/conf/redis.conf 全部执行命令如下 docker run -it -d --name redis -p 6379:6379 --net mynet --ip 172.18.0.9 -m 400m -v /root/redis/conf:/usr/local/etc/redis -e TXAsia/Shangehai redis redis-server /usr/local/etc/redis/redis.conf 部署…

Visual studio下使用 Wix 打包 C#/WPF 程序的中文安装包

Visual studio下使用 Wix 打包 C#/WPF 程序的中文安装包 1 下载并安装 Wix Toolset1.1 下载WIX Toolset1.2 安装1.3 配置系统环境变量path1.4 找不到 WiX 工具 candle.exe2 安装Visual studio 20202,并安装插件2.1 下载并安装 Visual Studio2.2 步骤二:安装 Wix v3 扩展插件3 …

Docker安装遇到问题:curl: (7) Failed to connect to download.docker.com port 443: 拒绝连接

问题描述 首先&#xff0c;完全按照Docker官方文档进行安装&#xff1a; Install Docker Engine on Ubuntu | Docker Docs 在第1步&#xff1a;Set up Dockers apt repository&#xff0c;执行如下指令&#xff1a; sudo curl -fsSL https://download.docker.com/linux/ubu…

免杀笔记 ---> Session0--DLL注入

刚更新完上一篇&#xff0c;于是我们就马不停蹄的去跟新下一篇&#xff01;&#xff01; Session0注入 &#xff1a;&#xff1a; 各位看官如果觉得还不错的可以给博主点个赞&#x1f495;&#x1f495; 这次&#xff0c;我把这个脚本直接传到Github上了 喜欢的师傅点个Star噢…

android新闻app(二)

新闻详细页&#xff1a; 历史浏览记录SQList&#xff1a; 分类&#xff1a; 历史浏览记录主体UI和详细&#xff1a; 侧边栏&#xff1a; 参考&#xff1a;浩宇开发

QWidget窗口抗锯齿圆角的一个实现方案(支持子控件)2

QWidget窗口抗锯齿圆角的一个实现方案&#xff08;支持子控件&#xff09;2 本方案使用了QGraphicsEffect&#xff0c;由于QGraphicsEffect对一些控件会有渲染问题&#xff0c;比如列表、表格等&#xff0c;所以暂时仅作为研究&#xff0c;优先其他方案 在之前的文章中&#…

SQL面试题-留存率计算

表定义&#xff1a; create table if not exists liuliang_detail (user_id string comment ,record_time string comment yyyymmdd hh:mi:ss ) comment 流量明细表 ; 方法一&#xff1a; 计算的是整段时间范围内&#xff0c;每一天为基准的所有的留存1、2、7天的用户数。 …

Java 基础知识之 switch 语句和 yield 关键字

传统 switch 语句 传统的 switch 语句我们已经写了一万遍了&#xff0c;以下是一个典型的 switch 语句&#xff1a; int dayOfWeek 3; switch (dayOfWeek) {case 1:System.out.println("星期一");break;case 2:System.out.println("星期二");break;case…

【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【17】认证服务01—短信/邮件/异常/MD5

持续学习&持续更新中… 守破离 【雷丰阳-谷粒商城 】【分布式高级篇-微服务架构篇】【17】认证服务01 环境搭建验证码倒计时短信服务邮件服务验证码短信形式&#xff1a;邮件形式&#xff1a; 异常机制MD5参考 环境搭建 C:\Windows\System32\drivers\etc\hosts 192.168.…

昇思25天学习打卡营第09天 | 保存与加载

昇思25天学习打卡营第09天 | 保存与加载 在训练网络模型的过程中&#xff0c;通常希望保存中间状态和最后的结果&#xff0c;用于后续的模型微调、推理和部署。 文章目录 昇思25天学习打卡营第09天 | 保存与加载定义网络保存模型加载模型保存MindIR加载MindIR总结打卡 定义网…

RUST 编程语言 绘制随机颜色图片 画圆形 画矩形 画直线

什么是Rust Rust是一种系统编程语言&#xff0c;旨在提供高性能和安全性。它是由Mozilla和其开发社区创建的开源语言&#xff0c;设计目标是在C的应用场景中提供一种现代、可靠和高效的选择。Rust的目标是成为一种通用编程语言&#xff0c;能够处理各种计算任务&#xff0c;包…

#数据结构 顺序表

线性表 顺序表 每种结构都有它存在意义 线性表的顺序存储实现指的是用一组连续的存储单元存储线性表的数据元素。 概念 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性表&#xff0c;一般情况下采用数组存储。在数组上完成数据的增查改删。 逻辑结构&#…