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

太平洋大西洋水流问题

力扣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.mfbz.cn/a/779551.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

调度系统揭秘(下):调度算法与架构设计

文章目录 一、调度算法1.1、广度优先:1.2、深度优先1.3、总结广度优先搜索&#xff08;BFS&#xff09;深度优先搜索&#xff08;DFS&#xff09; 二、架构设计2.1、Master/Slave架构优劣分析 2.2、Leader架构优劣分析 2.3、总结 一、调度算法 在调度系统中&#xff0c;调度算…

【】AI八股-神经网络相关

Deep-Learning-Interview-Book/docs/深度学习.md at master amusi/Deep-Learning-Interview-Book GitHub 网上相关总结&#xff1a; 小菜鸡写一写基础深度学习的问题&#xff08;复制大佬的&#xff0c;自己复习用&#xff09; - 知乎 (zhihu.com) CV面试问题准备持续更新贴 …

本安防爆手机:危险环境下的安全通信解决方案

在石油化工、煤矿、天然气等危险环境中&#xff0c;通信安全是保障工作人员生命安全和生产顺利进行的关键。防爆智能手机作为专为这些环境设计的通信工具&#xff0c;提供了全方位的安全通信解决方案。 防爆设计与材料&#xff1a; 防爆智能手机采用特殊的防爆结构和材料&…

机械硬盘故障分析及损坏处理(坏道屏蔽)

机械硬盘故障分析&#xff1a; 1、加电后没有声音就是电机不转&#xff0c;是电路问题&#xff0c;更换电路板解决。 2、加电后电机转&#xff0c;有连续敲击声音&#xff0c;或有异响&#xff0c;磁头损坏或机械故障。 3、加电后电机转&#xff0c;运行正常&#xff0c;BIOS无…

建立数据通路(一)

指令周期(Instruction Cycle) 指令种类 Fetch(取得指令) 也就是从PC寄存器里找到对应的指令地址&#xff0c;根据指令地址从内存里把具体的指令&#xff0c;加载到指令寄存器中然后把PC寄存器自增&#xff0c;好在未来执行下一条指令 Decode(指令译码) 也就是根据指令寄存器里…

Apache Seata新特性支持 -- undo_log压缩

本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 本文来自 Apache Seata官方文档&#xff0c;欢迎访问官网&#xff0c;查看更多深度文章。 Apache Seata新特性支持 – undo_log压缩 Seata新特性支持 – undo_log压缩 现状 & 痛点…

类与对像(1)

好几个月没有写了&#xff0c;差了好多&#xff0c;这些天补回来吧。 接下来&#xff0c;让我们正式步入C与C语言开始不同的地方。 我对类的理解&#xff1a;类是对于具有相同或相似属性的数据集合。 类的关键词&#xff1a;class&#xff0c;public&#xff0c;protected&a…

2024年加密货币市场展望:L1、L2、LSD、Web3 和 GameFi 板块的全面分析与预测

随着区块链技术的快速发展&#xff0c;加密货币市场在2024年继续展现出蓬勃的生机和创新的潜力。本文将深入分析L1、L2、LSD、Web3和GameFi这五大板块的发展趋势和预测&#xff0c;帮助投资者和爱好者更好地理解和把握市场机遇。 一、L1&#xff1a;基础层协议的持续进化 L1&a…

python自动化办公之cryptography加密解密

目录 用到的库 实现效果 代码部分 1、加密2024.txt文件 2、解密2024.txt文件 用到的库 cryptography 实现效果 加密文件和解密文件 代码部分 1、加密2024.txt文件 # 加密 from cryptography.fernet import Fernet # 生成加密密钥 keyFernet.generate_key() cipher_s…

K8S 部署 EFK

安装说明 系统版本为 Centos7.9 内核版本为 6.3.5-1.el7 K8S版本为 v1.26.14 ES官网 开始安装 本次安装使用官方ECK方式部署 EFK&#xff0c;部署的是当前的最新版本。 在 Kubernetes 集群中部署 ECK 安装自定义资源 如果能打开这个网址的话直接用这个命令安装,打不开的话…

创建一个不带框架的javaweb工程

点击新建 选择Maven&#xff0c;然后在Archetype里面选择 webapp选项&#xff08;注意这里需要配置好Maven的环境 如果没配好Maven引入依赖的时候会引不进来&#xff09; 如果Maven配置之后就会显示配置成功 然后我们要配置tomacat的依赖 jde选择默认 然后点击部署 选择工件&a…

高阶算法班从入门到精通之路课程

本课程旨在帮助学员深入理解算法与数据结构的核心概念&#xff0c;从而掌握高级算法设计与分析技能。每集课程内容精心设计&#xff0c;涵盖了常用数据结构、经典算法及其应用场景等方面的深度讲解&#xff0c;同时通过大量实例演练&#xff0c;帮助学员提升解决实际编程难题的…

2000-2022年地级市数字经济指数(含控制变量)

2000-2022年地级市数字经济指数&#xff08;含控制变量&#xff09; 目录 数字经济对区域经济发展的影响实证研究 一、引言 二、文献综述 三、数据来源与变量说明 四、实证模型 五、程序代码与运行结果 数字经济对区域经济发展的影响实证研究 摘要&#xff1a; 本文旨在…

【分布式计算框架 MapReduce】高级编程—搜索日志数据分析

目录 一、对于 sogou_500w_utf 数据&#xff0c;使用 MapReduce 编程模型完成对以下数据的分析任务 1. 统计 2011-12-30 日搜索记录&#xff0c;每个时间段的搜索次数 &#xff08;1&#xff09;运行截图 &#xff08;2&#xff09; 源代码 2. 统计 2011-12-30 日 3 点至 …

C++类与对象

1. stack声明与定义 引入构造器实现 自定义 栈大小 // constructor构造器 // 1. 与类名相同&#xff0c;无返回值&#xff0c;被系统生成对象时自动调用&#xff0c;用于初始化 // 2. 可以有参数&#xff0c;构造器的重载&#xff0c;默认参数&#xff0c;重载和默认参数不同…

2024阿里国际春招笔试

第一题 0 解题思路&#xff1a; 数据范围很大&#xff0c;肯定得找规律。 当n1时&#xff0c;0&#xff0c;1&#xff0c;结果为0 当n2时&#xff0c;00&#xff0c;01&#xff0c;10&#xff0c;11&#xff0c;结果为1 当n3时&#xff0c;000&#xff0c;001&#xff0c;010&a…

38 IO流

目录 C语言的输入和输出流是什么CIO流stringstream的简单介绍 1. C语言的输入与输出 C语言中我们用到的最频繁的输出方式是scanf和printf&#xff0c;scanf&#xff1a;从标准输入设备&#xff08;键盘&#xff09;读取数据&#xff0c;并将值存在变量中。printf&#xff1a;…

Linux 系统管理4——账号管理

一、用户账号管理 1、用户账号概述 &#xff08;1&#xff09;用户账号的常见分类&#xff1a; 1>超级用户&#xff1a;root uid0 gid0 权限最大。 2>普通用户&#xff1a;uid>500 做一般权限的系统管理&#xff0c;权限有限。 3>程序用户&#xff1a;1<uid&l…

昇思25天学习打卡营第12天 | LLM原理和实践:MindNLP ChatGLM-6B StreamChat

1. MindNLP ChatGLM-6B StreamChat 本案例基于MindNLP和ChatGLM-6B实现一个聊天应用。 ChatGLM-6B应该是国内第一个发布的可以在消费级显卡上进行推理部署的国产开源大模型&#xff0c;2023年3月就发布了。我在23年6月份的时候就在自己的笔记本电脑上部署测试过&#xff0c;当…

2024年江苏省研究生数学建模科研创新实践大赛C题气象数据高精度融合技术研究论文和代码分析

经过不懈的努力&#xff0c; 2024年江苏省研究生数学建模科研创新实践大赛C题气象数据高精度融合技术研究论文和代码已完成&#xff0c;代码为C题全部问题的代码&#xff0c;论文包括摘要、问题重述、问题分析、模型假设、符号说明、模型的建立和求解&#xff08;问题1模型的建…