LeetCode 200:岛屿数量(图的简化版之网格结构上的BFS、DFS)

news2024/10/7 8:20:36

图的BFS和DFS

首先让我们回顾一下图的BFS和DFS遍历。可以看到这种BFS和DFS板子适用于图形状,或者说结构已经确定,即我们遍历的时候只需要从根节点从上往下遍历即可,不用考虑这个节点有几个叶子节点,是否会遍历到空节点等边界情况的问题。

public class Graph{
    public HashMap<Integer,Node>nodes;//点集,第一个参数是点的编号。和Node类中的value一致。不一定是Integer类型的,要看具体的题,有的题点编号为字母。
    public HashSet<Edge>edges;//边集

    public Graph(){
        nodes = new HashMap<>();
        edges = new HashSet<>();
    }
}

public class Node{
    public int value;//点的编号,不一定是Integer类型的,要看具体的题,有的题点编号为字母。
    public int in;//入度
    public int out;//出度
    public ArrayList<Node>nexts;//出去的边直接相连的邻居。
    public ArrayList<Edge>edges//出去的边

    public Node(int value){
        this.value=value;
        in = 0;
        out = 0;
        nexts = new ArrayList<>();
        edges = new ArrayList<>();
    }
}

public class Edge{
    public int weight;//边上权重
    public Node from;
    public Node to;

    public Edge(int weight,Node from,Node to){
        this.weight=weight;
        this.from=from;
        this.to=to;
    }
}

public static void bfs(Node node){
    if(node==null) return;
    Queue<Node> queue = new LinkedList<>();
    HashSet<Node> set = new HashSet<>();
    queue.add(node);
    set.add(node);
    while(!queue.isEmpty()){
        Node cur = queue.poll();
        /*  具体的处理逻辑
            (宽搜一般是结点入队列后再处理)
        */
        for(Node next: cur.nexts){
            if(!set.contains(next)){//如果set中没有,那么说明这个next结点没有被访问过
            queue.add(next);//扔到队列里
            set.add(next);//并且标记访问
            }
        }
    }
}

public static void dfs(Node node){
    if(node==null) return;
    Stack<Node> stack = new Stack<>();
    HashSet<Node> set = new HashSet<>();
    stack.add(node);
    set.add(node);
    /*具体的处理逻辑(深搜一般是结点入栈前就进行处理)*/
    while(!stack.isEmpty()){
        Node cur = stack.pop();
        for(Node next:cur.nexts){
            if(!set.contains(next)){
                stack.push(cur);//在这里需要把cur和next两个结点同时入栈是因为
                stack.push(next);//想在栈里保持深度搜索的路径。这次搜索相比于上一次搜索,在栈中就多了一个next结点。
                set.add(cur);
                set.add(next);
                /*具体的处理逻辑 */
                break;//之所以立马break是因为深搜每次只走一步,不像宽搜每次走一层。
            }
        }
    }
}

网格结构的dfs、bfs(岛屿问题)

网格结构

但是网格结构的题不一样,一般这种题不会给出图的结构或者形状,而且不会给出根节点的具体位置,那么我们由网格结构构造图结构也比较麻烦。
首先让我们了解一下什么是网格结构。以下内容来自于力扣用户:nettee

网格结构是由 m×n个小方格组成一个网格,每个小方格与其上下左右四个方格认为是相邻的,要在这样的网格上进行某种搜索。
在这里插入图片描述
网格结构要比二叉树结构稍微复杂一些,它其实是一种简化版的图结构。

网格结构的dfs

要写好网格上的 DFS 遍历,我们首先要理解二叉树上的 DFS 遍历方法,再类比写出网格结构上的 DFS 遍历。我们写的二叉树 DFS 遍历一般是这样的:

void traverse(TreeNode root) {
    // 判断边界情况
    if (root == null) {
        return;
    }
    // 访问两个相邻结点:左子结点、右子结点
    traverse(root.left);
    traverse(root.right);
}

可以看到,二叉树的 DFS 有两个要素:「访问相邻结点」和「判断边界情况」。
对于网格上的 DFS,我们完全可以参考二叉树的 DFS,写出网格 DFS 的两个要素:

首先,网格结构中的格子有多少相邻结点?答案是上下左右四个。对于格子 (r, c) 来说(r 和 c 分别代表行坐标和列坐标),四个相邻的格子分别是 (r-1, c)、(r+1, c)、(r, c-1)、(r, c+1)。换句话说,网格结构是「四叉」的。
在这里插入图片描述其次,网格 DFS 中的边界情况是什么?就是超出网格m*n的范围或者遍历到已经遍历过的节点胡总和遍历到不能遍历的点(岛屿问题中的海洋格子)。
这样,我们得到了网格 DFS 遍历的框架代码:

板子
void dfs(int[][] grid, int r, int c) {
    // 判断边界情况
    // 如果坐标 (r, c) 超出了网格范围,直接返回
    if (!inArea(grid, r, c)) return;
    if(vis[r][c]==true||grid[r][c]==0) return;
    // 访问上、下、左、右四个相邻结点
    dfs(grid, r - 1, c);
    dfs(grid, r + 1, c);
    dfs(grid, r, c - 1);
    dfs(grid, r, c + 1);
}

// 判断坐标 (r, c) 是否在网格中
boolean inArea(int[][] grid, int r, int c) {
    return 0 <= r && r < grid.length 
        	&& 0 <= c && c < grid[0].length;
}

其中的vis[r][c]==true的判断可以试不同的题而作改动,像是一般有固定数值的网格问题,譬如0表示海洋,1表示陆地的岛屿问题,我们就可以每遍历一个格子后修改其值为2,表示这个是已经遍历过的陆地格子。然后直接判断if(grid[r][c]!=1)这样就不需要额外的一个vis数组去存储遍历信息了。

LC 200题解

对于LC 200该题而言,代码就是

public int numIslands(char[][] grid) {
        int num = 0;
        for(int r=0;r<grid.length;r++){
            for(int c=0;c<grid[0].length;c++){
                if(grid[r][c]=='1'){//外层的话我需要找到所有陆地格子,而不只是一个陆地格子
                //因为可能有多个不连通子图
                    dfs(grid,r,c);
                    num++;
                }
            }
        }
        return num;
    }
    public void dfs(char[][] grid, int r,int c){
        if(!ifCouldVis(grid,r,c)) return;
        grid[r][c]='2';
        dfs(grid,r+1,c);
        dfs(grid,r,c+1);
        dfs(grid,r-1,c);
        dfs(grid,r,c-1);
    }

    public boolean ifCouldVis(char[][] grid, int r,int c){
        return r>=0&&c>=0&&r<grid.length&&c<grid[0].length&&grid[r][c]=='1';
    }

执行用时分布3ms,击败68.78%使用 Java 的用户。
应该暂时不用优化了。

同类型题

LC130:被围绕的区域,见本人的另一篇博客
http://t.csdnimg.cn/67a6y

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

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

相关文章

如果品牌刚刚开始,切入私域社群团购,快团团是最好的选择

如果品牌刚刚开始&#xff0c;切入私域社群团购&#xff0c;快团团是最好的选择&#xff0c;借力新渠道社群团购&#xff0c;快团团&#xff0c;成就你的新品牌&#xff0c; 社群团购平台本身就有大量的信任你平台的流量&#xff0c;这个流量基数是巨大的。 你要知道的是&…

【DDD】学习笔记-服务行为模型

如果将服务视为一种行为&#xff0c;就必然需要考虑客户端与服务之间的协作。服务行为的调用者可以认为是服务消费者&#xff08;Service Consumer&#xff09;&#xff0c;提供服务行为的对象则是服务提供者&#xff08;Service Provider&#xff09;。为了服务消费者能够发现…

C++ STL: vector使用及源码剖析

vector使用 vector定义 语句 作用 vector<int> a(n); 指定容器大小为n vector<int> a(n, x); 指定容器大小为n&#xff0c;并初始化所有元素为x vector<vector<int>> a(m, vector<int>(n)); m行n列的二维数组&#xff0c;可以直接…

游戏开发-会飞的小鸟(已完结,附源码)

游戏开发-会飞的小鸟&#xff08;已完结&#xff0c;附源码&#xff09; 你将学到的课程链接详细介绍 你将学到的 掌握Java编程的基本技能开发出自己的“会飞的小鸟”游戏对面向对象编程有深刻的理解学会运用常见算法和数据结构解决问题能够独立调试和优化自己的代码 课程链接…

(2)(2.14) SPL Satellite Telemetry

文章目录 前言 1 本地 Wi-Fi&#xff08;费用&#xff1a;30 美元以上&#xff0c;范围&#xff1a;室内&#xff09; 2 蜂窝电话&#xff08;费用&#xff1a;100 美元以上&#xff0c;范围&#xff1a;蜂窝电话覆盖区域&#xff09; 3 手机卫星&#xff08;费用&#xff…

Android.mk 语法详解

一.Android.mk简介 Android.mk 是Android 提供的一种makefile 文件,注意用来编译生成&#xff08;exe&#xff0c;so&#xff0c;a&#xff0c;jar&#xff0c;apk&#xff09;等文件。 二.Android.mk编写 分析一个最简单的Android.mk LOCAL_PATH : $(call my-dir) //定义了…

[Python] opencv - 什么是直方图?如何绘制图像的直方图?如何对直方图进行均匀化处理?

什么是直方图&#xff1f; 直方图是一种统计图&#xff0c;用于展示数据的分布情况。它将数据按照一定的区间或者组进行划分&#xff0c;然后计算在每个区间或组内的数据频数或频率&#xff08;即数据出现的次数或占比&#xff09;&#xff0c;然后用矩形或者柱形图的形式将这…

『运维备忘录』之 TAR 命令详解

运维人员不仅要熟悉操作系统、服务器、网络等只是&#xff0c;甚至对于开发相关的也要有所了解。很多运维工作者可能一时半会记不住那么多命令、代码、方法、原理或者用法等等。这里我将结合自身工作&#xff0c;持续给大家更新运维工作所需要接触到的知识点&#xff0c;希望大…

绕过安全狗

本节我们想要绕过的安全狗版本为v4.023957 &#xff0c;它是网站安全狗的Apache版。 首先搭建环境。渗透环境选用DVWA漏洞集成环境&#xff0c;下载地址 为http://www.dvwa.co.uk/ 。DVWA是一款集成的渗透测试演练环境&#xff0c;当刚刚入门 并且找不到合适的靶机时&#xff…

c++ 类,第一篇章,初始化列表 (详细)

快过年啦&#xff01;雀儿在这里提前祝大家新年快乐&#xff01; 初始化&#xff0c;就是在一个变量在创建的时候被赋值&#xff0c;一共有四种可能 //X是类名&#xff0c;a是对象名&#xff0c;v是初始值 X a{v}; X a1{v}; X a2v; X a3(v);一共四种写法&#xff0c;如上。 第…

正点原子--STM32基本定时器学习笔记(1)

目录 1. 定时器概述 1.1 软件定时原理 1.2 定时器定时原理 1.3 定时器分类 1.4 定时器特性表 1.5 基本、通用、高级定时器的功能整体区别 2. 基本定时器简介 3. 基本定时器框图 时钟树分析 这部分是笔者对基本定时器的理论知识进行学习与总结&#xff01;主要记录学习…

Leaf——美团点评分布式ID生成系统

0.普通算法生成id的缺点 1.Leaf-segment数据库方案 第一种Leaf-segment方案&#xff0c;在使用数据库的方案上&#xff0c;做了如下改变&#xff1a; - 原方案每次获取ID都得读写一次数据库&#xff0c;造成数据库压力大。改为利用proxy server批量获取&#xff0c;每次获取一…

基于spring cloud alibaba的微服务平台架构规划

平台基础能力规划&#xff08;继续完善更新…&#xff09; 一、统一网关服务&#xff08;独立服务&#xff09; 二、统一登录鉴权系统管理&#xff08;独立服务&#xff09; 1.统一登录 2.统一鉴权 3.身份管理 用户管理 角色管理 业务系统和菜单管理 部门管理 岗位管理 字典管…

一步步建立一个C#项目(连续读取S7-1200PLC数据)

这篇博客作为C#的基础系列,和大家分享如何一步步建立一个C#项目完成对S7-1200PLC数据的连续读取。首先创建一个窗体应用。 1、窗体应用 2、配置存储位置 3、选择框架 拖拽一个Button,可以选择视图菜单---工具箱 4、工具箱 拖拽Lable控件和TextBook控件 5、拖拽控件 接下来…

算法day12

算法day12 二叉树理论基础114 二叉树的前序遍历145 二叉树的后序遍历94 二叉树的中序遍历迭代法 二叉树理论基础 直接看代码随想录就完事了&#xff0c;之前考研也学过&#xff0c;大概都能理解 我这里就说说代码层面的。 二叉树的存储&#xff1a; 1、链式存储&#xff1a;这…

简单实验 spring cloud gateWay 路由实验 实验

1.概要 1.1 说明 微服务统一网关实验&#xff0c;这里简单实验一下路由的功能 1.2 实验步骤&#xff0c;使用下面这个工程作为基础工程添加了一个gateWay做如下使用 简单实践 spring cloud nacos nacos-server-2.3.0-CSDN博客 2 代码 2.1 工程文件 <?xml version&quo…

【Linux取经路】探寻shell的实现原理

文章目录 一、打印命令行提示符二、读取键盘输入的指令三、指令切割四、普通命令的执行五、内建指令执行5.1 cd指令5.2 export指令5.3 echo指令 六、结语 一、打印命令行提示符 const char* getusername() // 获取用户名 {return getenv("USER"); }const char* geth…

生成式学习,特别是生成对抗网络(GANs),存在哪些优点和缺点,在使用时需要注意哪些注意事项?

生成对抗网络&#xff08;GANs&#xff09; 1. 生成对抗网络&#xff08;GANs&#xff09;的优点&#xff1a;2. 生成对抗网络&#xff08;GANs&#xff09;的缺点&#xff1a;3. 使用生成对抗网络&#xff08;GANs&#xff09;需要注意的问题 1. 生成对抗网络&#xff08;GANs…

学生管理系统(javaSE第一阶段项目)

JavaSE第一阶段项目_学生管理系统 1.项目介绍 此项目是JavaSE第一阶段的项目,主要完成学生对象在数组中的增删改查,大家可以在此项目中发挥自己的想象力做完善,添加其他功能等操作,但是重点仍然是咱们前9个模块的知识点2.项目展示 2.1.添加功能 2.2.查看功能 2.3.修改功能 2…