代码随想录算法训练Day57|LeetCode200-岛屿数量、LeetCode695-岛屿的最大面积

news2024/9/22 13:44:15

岛屿数量

题目描述

力扣200-岛屿数量

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

示例 2:

输入:grid = [
  ["1","1","0","0","0"],
  ["1","1","0","0","0"],
  ["0","0","1","0","0"],
  ["0","0","0","1","1"]
]
输出:3

图一

本题思路,是用遇到一个没有遍历过的节点陆地,计数器就加一,然后把该节点陆地所能遍历到的陆地都标记上。在遇到标记过的陆地节点和海洋节点的时候直接跳过。 这样计数器就是最终岛屿的数量。

那么如果把节点陆地所能遍历到的陆地都标记上呢,就可以使用 DFS,BFS或者并查集。

广度优先搜索 BFS

不少同学用广搜做这道题目的时候,超时了。 这里有一个广搜中很重要的细节:

根本原因是==只要 加入队列就代表 走过,就需要标记,而不是从队列拿出来的时候再去标记走过==。

很多同学可能感觉这有区别吗?

如果从队列拿出节点,再去标记这个节点走过,就会发生下图所示的结果,会导致很多节点重复加入队列。

图二

 `visited[x][y] = true;` 放在的地方,着去取决于我们对 代码中队列的定义,队列中的节点就表示已经走过的节点。 **所以只要加入队列,立即标记该节点走过**

本题完整广搜代码:

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

    private void bfs(char[][] grid, boolean[][] visited, int x, int y) {//用于将当前陆地相连的陆地都进行标记
        Queue<int[]> queue = new LinkedList<>();
        queue.add(new int[] { x, y });
        visited[x][y] = true; // 只要加入队列,立刻标记
        while (!queue.isEmpty()) {
            int[] cur = queue.poll();
            int curx = cur[0];// 取出当前节点(curx,cury)
            int cury = cur[1];
            // 遍历四个方向,如果相邻节点(nextx,nexty)在网格内切未被访问过,并且其是陆地(1),则将其加入到队列,并将其标记为已访问
            for (int[] d : dir) {
                int nextx = curx + d[0];
                int nexty = cury + d[1];
                if (nextx < 0 || nextx >= grid.length || nexty < 0 || nexty >= grid[0].length)
                    continue; // 越界了,直接跳过
                if (!visited[nextx][nexty] && grid[nextx][nexty] == '1') {
                    queue.add(new int[] { nextx, nexty });
                    visited[nextx][nexty] = true; // 只要加入队列立刻标记
                }
            }
        } // 循环直到队列为空,即所有与起始点连通的陆地都被标记为已访问
    }

    public int numIslands(char[][] grid) {
        int n = grid.length;
        int m = grid[0].length;
        boolean[][] visited = new boolean[n][m];// 二维布尔数组visited,用于标记网格中每个位置是否已被访问过

        int result = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (!visited[i][j] && grid[i][j] == '1') {// 当前位置 未访问过的且是陆地,岛屿数量+1
                    result++; // 遇到没访问过的陆地,+1
                    bfs(grid, visited, i, j); // 将与其链接的陆地都标记上 true
                }
            }
        }
        return result;
    }
}

深度优先搜索 DFS—模板

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

    private void dfs(char[][] grid, boolean[][] visited, int x, int y) {
        for (int[] d : dir) {
            int nextx = x + d[0];
            int nexty = y + d[1];
            if (nextx < 0 || nextx >= grid.length || nexty < 0 || nexty >= grid[0].length) continue;  // 越界了,直接跳过
            if (!visited[nextx][nexty] && grid[nextx][nexty] == '1') { // 没有访问过的同时是陆地的
                visited[nextx][nexty] = true; 
                dfs(grid, visited, nextx, nexty);
            } 
        }
    }

    public int numIslands(char[][] grid) {
        int n = grid.length;
        int m = grid[0].length;
        boolean[][] visited = new boolean[n][m];

        int result = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (!visited[i][j] && grid[i][j] == '1') { 
                    visited[i][j] = true;
                    result++; // 遇到没访问过的陆地,+1
                    dfs(grid, visited, i, j); // 将与其链接的陆地都标记上 true
                }
            }
        }
        return result;
    }
}

下面的代码使用的是深度优先搜索 DFS 的做法。为了统计岛屿数量同时不重复记录,每当我们搜索到一个岛后,就将这个岛 “淹没” —— 将这个岛所占的地方从 “1” 改为 “0”,这样就不用担心后续会重复记录这个岛屿了。而 DFS 的过程就体现在 “淹没” 这一步中。详见代码:

public int numIslands(char[][] grid) {
    int res = 0; //记录找到的岛屿数量
    for(int i = 0;i < grid.length;i++){
        for(int j = 0;j < grid[0].length;j++){
        	//找到“1”,res加一,同时淹没这个岛
            if(grid[i][j] == '1'){
                res++;
                dfs(grid,i,j);
            }
        }
    }
    return res;
}
//使用DFS“淹没”岛屿
public void dfs(char[][] grid, int i, int j){
	//搜索边界:索引越界或遍历到了"0"
    if(i < 0 || i >= grid.length || j < 0 || j >= grid[0].length || grid[i][j] == '0') return;
    //将这块土地标记为"0"
    grid[i][j] = '0';
    //根据"每座岛屿只能由水平方向或竖直方向上相邻的陆地连接形成",对上下左右的相邻顶点进行dfs
    dfs(grid,i - 1,j);
    dfs(grid,i + 1,j);
    dfs(grid,i,j + 1);
    dfs(grid,i,j - 1);
}

岛屿的最大面积

题目描述

力扣695-岛屿的最大面积

给你一个大小为 m x n 的二进制矩阵 grid

岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻(斜角度的不算)。你可以假设 grid 的四个边缘都被 0(代表水)包围着。

岛屿的面积是岛上值为 1 的单元格的数目。

计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0

示例 1:

img

输入:grid = [[0,0,1,0,0,0,0,1,0,0,0,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,1,1,0,1,0,0,0,0,0,0,0,0],[0,1,0,0,1,1,0,0,1,0,1,0,0],[0,1,0,0,1,1,0,0,1,1,1,0,0],[0,0,0,0,0,0,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,0,0,0,0,0,0,1,1,0,0,0,0]]
输出:6
解释:答案不应该是 11 ,因为岛屿只能包含水平或垂直这四个方向上的 1 。

示例 2:

输入:grid = [[0,0,0,0,0,0,0,0]]
输出:0

解题思路

这道题目也是 dfs bfs基础类题目,就是搜索每个岛屿上“1”的数量,然后取一个最大的。

本题思路上比较简单,难点其实都是 dfs 和 bfs的理论基础,关于理论基础我在这里都有详细讲解 :

DFS理论基础(opens new window)

BFS理论基础

根据BFS模板

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

    void bfs(char[][] grid, boolean[][] visited, int x, int y) {
        Queue<int[]> queue = new LinkedList<>(); // 定义队列
        queue.offer(new int[]{x, y}); // 起始节点加入队列
        visited[x][y] = true; // 只要加入队列,立刻标记为访问过的节点
        while (!queue.isEmpty()) { // 开始遍历队列里的元素
            int[] cur = queue.poll(); // 从队列取元素
            int curx = cur[0];
            int cury = cur[1]; // 当前节点坐标
            for (int i = 0; i < 4; i++) { // 开始向当前节点的四个方向左右上下去遍历
                int nextx = curx + dir[i][0];
                int nexty = cury + dir[i][1]; // 获取周围四个方向的坐标
                if (nextx < 0 || nextx >= grid.length || nexty < 0 || nexty >= grid[0].length) continue;  // 坐标越界了,直接跳过
                if (!visited[nextx][nexty]) { // 如果节点没被访问过
                    queue.offer(new int[]{nextx, nexty}); // 队列添加该节点为下一轮要遍历的节点
                    visited[nextx][nexty] = true; // 只要加入队列立刻标记,避免重复访问
                }
            }
        }
    }
}

BFS

//BFS
class Solution {
    int[][] dir = {
            { 0, 1 }, { 1, 0 }, { 0, -1 }, { -1, 0 }
    };

    int count;
    boolean visited[][];

    public int maxAreaOfIsland(int[][] grid) {
        int res = 0;
        visited = new boolean[grid.length][grid[0].length];

        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[0].length; j++) {
                if (visited[i][j] == false && grid[i][j] == 1) {
                    count = 0;
                    bfs(grid, i, j);
                    res = Math.max(res, count);
                }
            }
        }
        return res;
    }

    private void bfs(int[][] grid, int x, int y) {
        Queue<int[]> queue = new LinkedList<>(); // 定义队列
        queue.offer(new int[] { x, y }); // 起始节点加入队列
        visited[x][y] = true; // 只要加入队列,立刻标记为访问过的节点
        count++; // 将起始节点也算入岛屿面积中
        while (!queue.isEmpty()) { // 开始遍历队列里的元素
            int[] cur = queue.poll(); // 从队列取元素
            int curx = cur[0];
            int cury = cur[1]; // 当前节点坐标
            for (int i = 0; i < 4; i++) { // 开始向当前节点的四个方向左右上下去遍历
                int nextx = curx + dir[i][0];
                int nexty = cury + dir[i][1]; // 获取周围四个方向的坐标

                if (nextx < 0 || nextx >= grid.length || nexty < 0 || nexty >= grid[0].length)
                    continue;

                if (visited[nextx][nexty] == false && grid[nextx][nexty] == 1) {
                    queue.offer(new int[] { nextx, nexty }); // 队列添加该节点为下一轮要遍历的节点
                    visited[nextx][nexty] = true;
                    count++;
                }
            }
        }
    }

}

根据DFS模板

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

    private void dfs(char[][] grid, boolean[][] visited, int x, int y) {
        for (int[] d : dir) {
            int nextx = x + d[0];
            int nexty = y + d[1];
            if (nextx < 0 || nextx >= grid.length || nexty < 0 || nexty >= grid[0].length) continue;  // 越界了,直接跳过
            if (!visited[nextx][nexty] && grid[nextx][nexty] == '1') { // 没有访问过的同时是陆地的
                visited[nextx][nexty] = true; 
                dfs(grid, visited, nextx, nexty);
            } 
        }
    }

    public int numIslands(char[][] grid) {
        int n = grid.length;
        int m = grid[0].length;
        boolean[][] visited = new boolean[n][m];

        int result = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (!visited[i][j] && grid[i][j] == '1') { 
                    visited[i][j] = true;
                    result++; // 遇到没访问过的陆地,+1
                    dfs(grid, visited, i, j); // 将与其链接的陆地都标记上 true
                }
            }
        }
        return result;
    }
}

DFS

//DFS
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) {
        for (int[] d : dir) {
            int nextx = x + d[0];
            int nexty = y + d[1];
            if (nextx < 0 || nextx >= grid.length || nexty < 0 || nexty >= grid[0].length)
                continue; // 越界了,直接跳过
            if (!visited[nextx][nexty] && grid[nextx][nexty] == 1) { // 没有访问过的 同时 是陆地的
                visited[nextx][nexty] = true;
                count++;
                dfs(grid, visited, nextx, nexty);
            }
        }
    }

    public int maxAreaOfIsland(int[][] grid) {
        int n = grid.length;
        int m = grid[0].length;
        boolean[][] visited = new boolean[n][m];

        int result = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (!visited[i][j] && grid[i][j] == 1) {
                    count = 1; // 因为dfs处理下一个节点,所以这里遇到陆地了就先计数,dfs处理接下来的相邻陆地
                    visited[i][j] = true;
                    dfs(grid, visited, i, j); // 将与其链接的陆地都标记上 true
                    result = Math.max(result, count);
                }
            }
        }
        return result;
    }
}

ps:部分图片和代码来自代码随想录和Leetcode官网

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

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

相关文章

代码随想录-Day49

300. 最长递增子序列 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列 是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的 …

【数据结构(邓俊辉)学习笔记】高级搜索树01——伸展树

文章目录 1. 逐层伸展1. 1 宽松平衡1. 2 局部性1. 3 自适应调整1. 4 逐层伸展1. 5 实例1. 6 一步一步往上爬1. 7 最坏情况 2. 双层伸展2.1 双层伸展2.2 子孙异侧2.3 子孙同侧2.4 点睛之笔2.5 折叠效果2.6 分摊性能2.7 最后一步 3 算法实现3.1 功能接口3.2 伸展算法3.3 四种情况…

产品经理-工作流程及职能(6)

产品经理作为互联网项目的主心骨&#xff0c;连接着团队的所有成员&#xff08;开发、设计、运营、测试、市场等&#xff09; 用合理的产品规划和清晰的产品愿景带领大家前进&#xff0c;通过满足用户需求来创造属于自己的商业利益。 在通常情况下&#xff0c;PM需要对整个产品…

基于工业互联网的智慧矿山解决方案PPT(38页)

文章摘要 工业互联网与智慧矿山 基于工业互联网的新一代智慧矿山解决方案&#xff0c;将互联网和新一代IT技术与工业系统深度融合&#xff0c;形成关键的产业和应用生态&#xff0c;推动工业智能化发展。该方案以“四级、三层、两网、一平台”为总体框架&#xff0c;强调应用目…

深圳晶彩智能ESP32-2432S028R实时观察LVGL9效果

深圳晶彩智能ESP32-2432S028R概述&#xff1a; 深圳晶彩智能出品ESP32-32432S028R为2.8寸彩色屏采用分辨率320x240彩色液晶屏&#xff0c;驱动芯片是ILI9431。板载乐鑫公司出品ESP-WROOM-32&#xff0c;Flash 4M。型号尾部“R”标识电阻膜的感压式触摸屏&#xff0c;驱动芯片是…

容器:stack

以下是关于stack容器的一些总结&#xff1a; stack容器比较简单&#xff0c;主要包括&#xff1a; 1、构造函数&#xff1a;stack [staName] 2、添加、删除元素: push() 、pop() 3、获取栈顶元素&#xff1a;top() 4、获取栈的大小&#xff1a;size() 5、判断栈是否为空&#x…

深入刨析Redis存储技术设计艺术(二)

三、Redis主存储 3.1、存储相关结构体 redisServer:服务器 server.h struct redisServer { /* General */ pid_t pid; /* Main process pid. */ pthread_t main_thread_id; /* Main thread id */ char *configfile; /* Absolut…

图书管理系统 全栈项目分享

文章目录 项目简要说明项目开源地址b站视频演示技术栈部分效果展示 项目简要说明 本项目是我的数据库课设&#xff0c;个人感觉做得还行&#xff0c;目前项目开源&#xff0c;README文档里有项目的介绍和使用说明&#xff0c;这里就不一一赘述了 项目开源地址 github - libr…

nginx(三)—从Nginx配置熟悉Nginx功能

一、 Nginx配置文件结构 ... #全局块events { #events块... }http #http块 {... #http全局块server #server块{ ... #server全局块location [PATTERN] #location块{...}location [PATTERN] {...}}server{...}... #http全局块 …

NoSQL 之 Redis 配置与常用命令

一、关系型数据库与非关系型数据库 1、数据库概述 &#xff08;1&#xff09;关系型数据库 关系型数据库是一个结构化的数据库&#xff0c;创建在关系模型&#xff08;二维表格模型&#xff09;基础上&#xff0c;一般面向于记 录。 SQL 语句&#xff08;标准数据查询语言&am…

ONLYOFFICE 8.1——全新桌面编辑器,相见恨晚

目录 前言 一、链接 ONLYOFFICE 8.1版本 官网下载链接&#xff1a; ONLYOFFICE 在线工具&#xff1a; 下载版本推荐&#xff1a; 二、使用体验 1. 界面设计&#xff1a; 2. 文档编辑功能&#xff1a; 3. 电子表格功能&#xff1a; 4. 演示文稿功能&#xff1a; 5.PDF编…

使用 AlphaZero 和 Tabu 搜索查找越来越大的极值图

使用 AlphaZero 和 Tabu 搜索查找越来越大的极值图 AlphaZero 方法最佳研究&#xff08;第1部分&#xff09; 文章目录 一、说明二、引言三、预备知识四、方法4.1 AlphaZero4.2 禁忌搜索 五、实验与结果六、讨论七、附录A 一个例子B 问题背景 一、说明 人工智能的树和图的检索问…

Nacos源码分析:心跳机制、健康检查、服务发现、AP集群

文章目录 心跳机制与服务健康检查NacosClient端NacosServer端NacosServer端健康检查 服务发现NacosClient端NacosServer端 AP集群从源码启动集群心跳设计原理各节点状态同步服务实例数据同步服务实例状态变动同步 心跳机制与服务健康检查 官方文档&#xff1a;发送某个实例的心…

SpringBoot新手快速入门系列教程四:创建第一个SringBoot的API

首先我们用IDEA新建一个项目&#xff0c;请将这些关键位置按照我的设置设置一下 接下来我将要带着你一步一步创建一个Get请求和Post请求&#xff0c;通过客户端请求的参数&#xff0c;以json格式返回该参数{“message”:"Hello"} 1,先在IDE左上角把这里改为文件模式…

MySQL基础篇(三)数据库的修改 删除 备份恢复 查看连接情况

对数据库的修改主要指的是修改数据库的字符集&#xff0c;校验规则。 将test1数据库字符集改为gbk。 数据库的删除&#xff1a; 执行完该数据库就不存在了&#xff0c;对应数据库文件夹被删除&#xff0c;级联删除&#xff0c;里面的数据表全部被删除。 注意&#xff1a;不要随…

【IO】文件操作

&#x1f970;&#x1f970;&#x1f970;来都来了&#xff0c;不妨点个关注叭&#xff01; &#x1f449;博客主页&#xff1a;欢迎各位大佬!&#x1f448; 文章目录 1. 文件1.1 认识文件1.2 分清操作的是内存还是硬盘1.3 路径1.3.1 目录结构1.3.2 相对和绝对路径 1.4 文本文件…

Kafka系列之Kafka知识超强总结

一、Kafka简介 Kafka是什么 Kafka是一种高吞吐量的分布式发布订阅消息系统&#xff08;消息引擎系统&#xff09;&#xff0c;它可以处理消费者在网站中的所有动作流数据。 这种动作&#xff08;网页浏览&#xff0c; 搜索和其他用户的行动&#xff09;是在现代网络上的许多社…

Feign-未完成

Feign Java中如何实现接口调用&#xff1f;即如何发起http请求 前三种方式比较麻烦&#xff0c;在发起请求前&#xff0c;需要将Java对象进行序列化转为json格式的数据&#xff0c;才能发送&#xff0c;然后进行响应时&#xff0c;还需要把json数据进行反序列化成java对象。 …

基于顺序表的通讯录实现

一、前言 基于已经学过的顺序表&#xff0c;可以实现一个简单的通讯录。 二、通讯录相关头文件 //Contact.h #pragma once#define NAME_MAX 20 #define TEL_MAX 20 #define ADDR_MAX 20 #define GENDER_MAX 20typedef struct PersonInfo {char name[NAME_MAX];char gender[G…

node的下载、安装、配置和使用(node.js下载安装和配置、npm命令汇总、cnpm的使用)

天行健,君子以自强不息;地势坤,君子以厚德载物。 每个人都有惰性,但不断学习是好好生活的根本,共勉! 文章均为学习整理笔记,分享记录为主,如有错误请指正,共同学习进步。 愿将腰下剑,直为斩楼兰。 ——《塞下曲》 文章目录 一、node.js的下载、安装和配置1. node.js下…