关于【网格结构】岛屿类问题的通用解法DFS(深度遍历)遍历框架+回溯+剪枝总结

news2025/1/11 10:01:25

最近在刷力扣时遇见的问题,自己总结加上看了力扣大佬的知识总结写下本篇文章,我们所熟悉的 DFS(深度优先搜索)问题通常是在树或者图结构上进行的。而我们今天要讨论的 DFS 问题,是在一种「网格」结构中进行的。岛屿问题是这类网格 DFS 问题的典型代表。网格结构遍历起来要比二叉树复杂一些,如果没有掌握一定的方法,DFS 代码容易写得冗长繁杂。

目录

一、网格类问题的DFS遍历方法

1.1网格问题基本概念

 1.2 DFS的基本结构

1.3 如何避免重复遍历

二、岛屿经典例题

2.1 岛屿数量

2.2 岛屿的周长

2.3 单词搜索


一、网格类问题的DFS遍历方法

1.1网格问题基本概念

网格问题是由 m×n 个小方格组成一个网格,每个小方格与其上下左右四个方格认为是相邻的,要在这样的网格上进行某种搜索。岛屿问题是一类典型的网格问题。每个格子中的数字可能是 0 或者 1。 数字为1的格子连接起来就形成一个岛屿。

 1.2 DFS的基本结构

首先我们要确定访问的下一个节点以及停止的边界,那么首先网格结构中的每个格子可以向四周延伸,分别为上,下,左,右,对于格子(r,c)来说,对应的四个坐标分别为(r+1,c)、(r-1,c)、(r,c-1)、(r,c+1)。

 

 其次超出格子的边界是什么?是grid[r][c]出现下标越界异常的格子,也就是那些超出范围的格子。

 

 这样我们就得到了网格DFS遍历的框架代码:

public void infect(char[][] grid,int i ,int j){
//超出范围
        if (i < 0 || i >= grid.length || 
        j < 0 || j >= grid[0].length){
            return;
        }
//访问上,下,左,右
        infect(grid,i+1,j);
        infect(grid,i-1,j);
        infect(grid,i,j+1);
        infect(grid,i,j-1);
}

1.3 如何避免重复遍历

网格结构的 DFS 与二叉树的 DFS 最大的不同之处在于,遍历中可能遇到遍历过的结点。这是因为,网格结构本质上是一个「图」,我们可以把每个格子看成图中的结点,每个结点有向上下左右的四条边。在图中遍历时,自然可能遇到重复遍历结点。

 如何避免这样的重复遍历呢?答案是将已经遍历过的格子的值修改一下,每次走过一个格子就修改格子的值。以岛屿问题为例,我们需要在所有值为 1 的陆地格子上做 DFS 遍历。每走过一个陆地格子,就把格子的值改为 2,这样当我们遇到 2 的时候,就知道这是遍历过的格子了。

public void infect(char[][] grid,int i ,int j){
        if (i < 0 || i >= grid.length || 
        j < 0 || j >= grid[0].length ){
            return;
        }
        grid[i][j] = '2';
        infect(grid,i+1,j);
        infect(grid,i-1,j);
        infect(grid,i,j+1);
        infect(grid,i,j-1);
    }

二、岛屿经典例题

2.1 岛屿数量

岛屿类问题的通用解法、DFS 遍历框架 - 岛屿数量 - 力扣(LeetCode)

  • 0 —— 海洋格子
  • 1 —— 陆地格子(未遍历过)
  • 2 —— 陆地格子(已遍历过)
class Solution {
   public int numIslands(char[][] grid) {
        int islandNum = 0;
        for (int i = 0; i < grid.length; i++) {
            for (int j = 0; j < grid[0].length; j++) {
                if (grid[i][j] == '1'){
                    infect(grid,i,j);
                    islandNum++;
                }
            }
        }
        return islandNum;
    }
    
    public void infect(char[][] grid,int i ,int j){
        if (i < 0 || i >= grid.length || 
        j < 0 || j >= grid[0].length || grid[i][j] != '1'){
            return;
        }
        grid[i][j] = '2';
        infect(grid,i+1,j);
        infect(grid,i-1,j);
        infect(grid,i,j+1);
        infect(grid,i,j-1);
    }
}

2.2 岛屿的周长

463. 岛屿的周长 - 力扣(LeetCode)

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

网格中的格子 水平和垂直 方向相连(对角线方向不相连)。整个网格被水完全包围,但其中恰好有一个岛屿(或者说,一个或多个表示陆地的格子相连组成的岛屿)。

我们可以将岛屿的周长中的边分为两类,如下图所示。黄色的边是与网格边界相邻的周长,而蓝色的边是与海洋格子相邻的周长。

当我们的 dfs 函数因为「坐标 (r, c) 超出网格范围」返回的时候,实际上就经过了一条黄色的边;而当函数因为「当前格子是海洋格子」返回的时候,实际上就经过了一条蓝色的边。

 本题我们可以再次嵌套DFS的框架,但是要增加几处地方,实际上,岛屿的周长是计算岛屿全部的「边缘」,而这些边缘就是我们在 DFS 遍历中,dfs 函数返回的位置。

class Solution {
    public int islandPerimeter(int[][] grid) {
        for (int r = 0; r < grid.length; r++) {
            for (int c = 0; c < grid[0].length; c++) {
                if (grid[r][c] == 1){
                    return dfs(grid,r,c);
                }
            }
        }
        return 0;
    }

    public int dfs(int[][] grid,int r,int c){
        if (r < 0 || r >= grid.length ||
                c < 0 || c >= grid[0].length ){
            return 1;
        }
        if (grid[r][c] == 0){
            return 1;
        }
        if (grid[r][c] != 1){
            return 0;
        }
        grid[r][c] = 2;
        return dfs(grid,r-1,c)+dfs(grid,r+1,c)
                +dfs(grid,r,c-1)+dfs(grid,r,c+1);
    }
}

2.3 单词搜索

79. 单词搜索 - 力扣(LeetCode)

给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中,返回 true ;否则,返回 false 。

 本题在使用上面的DFS框架时,要加上回溯,因为当搜索一次时,会更改当前的格子的值,

如果我在DFS的过程中不改矩阵的状态,那我就会出现重复搜索的情况啊,这可不行,因此矩阵是必须得改的。那就只剩下一个办法了,每次DFS的过程中修改矩阵,DFS完了再把矩阵给改回去呗,这就是回溯!

那么什么是剪枝呢?所谓剪枝就是我在dfs的时候,如果已经找到一个正确的路径了,换句话说已经得到结果了,其实就没必要继续DFS了,直接返回结果即可。这就是剪枝。

给一下官方的说法:剪枝,就是减小树的规模,尽早排除搜索树中不必要的分支的一种手段。

class Solution {
    private boolean find;
    public boolean exist(char[][] board, String word) {
        if(board == null){
            return false;
        }
        int m = board.length,n = board[0].length;
        boolean[][] visited = new boolean[m][n];
        find = false;

        for(int i = 0;i < m;i++){
            for(int j = 0 ;j < n;j++){
                backtracking(i, j, board, word, visited, 0);
            }
        }
        return find;
    }

    /**
    * i,j,board:棋盘格及当前元素的坐标
    * word: 要搜索的目标单词
    * visited:记录当前格子是否已被访问过
    * pos: 记录目标单词的字符索引,只有棋盘格字符和pos指向的字符一致时,才有机会继续搜索接下来的字符;如果pos已经过了目标单词的尾部了,那么便说明找到目标单词了
    */
    public void backtracking(int i, int j, char[][] board, String word, boolean[][] visited, int pos){
         // 超出边界、已经访问过、已找到目标单词、棋盘格中当前字符已经和目标字符不一致了
        if(i < 0 || i >= board.length || j <0 || 
        j>=board[0].length || visited[i][j] || 
        find || board[i][j] != word.charAt(pos)){
            return;
        }
        if(pos == word.length()-1){
            find = true;
            return;
        }

        visited[i][j] = true;// 修改当前节点状态
        backtracking(i+1, j, board, word, visited, pos+1);  // 遍历子节点
        backtracking(i-1, j, board, word, visited, pos+1);
        backtracking(i, j+1, board, word, visited, pos+1);
        backtracking(i, j-1, board, word, visited, pos+1);
        visited[i][j] = false; // 撤销修改
    }
}

如果对你有帮助,麻烦双击三连一下,谢谢啦!

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

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

相关文章

FIFO IP Core

FIFO IP Core 先进先出的缓存器常常被用于数据的缓存&#xff0c;或者高速异步数据交互&#xff08;跨时钟信号传递&#xff09;和RAM和ROM的区别是没有地址线&#xff0c;无法指定地址 写时钟(Write Clock Domain)&#xff0c;读时钟写复位&#xff08;wr_rst)&#xff0c;读…

JAVA开发(史上最完整追本溯源JAVA历史、发展和学习)

(第二次世界大战1931-1945) 世界上最先进的技术往往是由于战争催生&#xff0c;在第二次世界大战中除了飞机&#xff0c;坦克和大炮的武器较量外&#xff0c;在隐秘战线的情报工作其实更为重要&#xff0c;在军队将领来往的电报中&#xff0c;为了防止军事情报的泄漏&#xff…

k8s编程operator实战之云编码平台——⑤项目完成、部署

文章目录1、效果展示2、保存用户状态和访问用户服务实现方案2.1 如何保存用户的状态2.1.1 解决保留安装的插件问题2.2 如何访问到用户在工作空间中启动的http服务2.2.1 code-server如何帮我们实现了用户程序的代理3、Operator功能实现3.1 使用KubeBuilder创建项目3.1.1 完善kin…

牛逼了!这是什么神仙面试宝典?半月看完25大专题,居然斩获阿里P7offer

这是什么神仙面试宝典&#xff1f;半月看完25大专题&#xff0c;居然斩获阿里P7offer&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;&#xff1f;容我小小的嘚瑟一下下啦~~这份神仙面试宝典总共有25大专题&#xff1a;专题一&#xff1a;JavaOOP面…

QT基础入门目录

&#x1f4a2;&#x1f4a2;&#x1f4a2;&#x1f4a2;&#x1f4a2;&#x1f4a2;&#x1f4a2;&#x1f4a2;&#x1f4a2;&#x1f4a2;&#x1f4a2;&#x1f4a2;&#x1f4a2;&#x1f4a2;&#x1f4a2;&#x1f4a2;目录总览&#x1f4a2;&#x1f4a2;&#x1f4a2;&…

CSGO服务器插件配置教程SourceModMetaMod插件深度解析

CSGO服务器插件配置教程SourceMod&MetaMod插件深度解析 我是艾西&#xff0c;再上一篇中我详细的说了csgo社区/私人服务器的搭建过程以及需要注意的一些事项&#xff0c;那么很多小伙伴对于插件可能还是会有些疑惑&#xff0c;主流的现在就是SourceMod&MetaMod插件&…

基于Ant DesignPro Vue实现通过SpringBoot后台加载自定义菜单- 前后端分离

基于Ant DesignPro Vue实现通过SpringBoot后台加载自定义菜单- 前后端分离 本文想基于Ant DesignPro Vue构建的前端SpringBoot实现的后端接口服务&#xff0c;实现前后端分离开发和独立运行&#xff0c;业务场景是登录认证&#xff0c;认证成功后返回该用户相应权限范围内可见的…

C语言杂记(指针篇)

指针篇 指针就是地址&#xff0c;地址就是指针 指针变量就是存放地址的变量 *号只有定义的时候表示定义指针变量&#xff0c;其他表示从地址里面取内容 通过指针的方法使main函数中的data1和data2发生数据交换。 #include <stdio.h> void chang_data(int *data1,int *da…

Flutter知识点(二)处理Json

flutter不支持反射&#xff0c;所以本来很简单的事情&#xff0c;一下子变复杂了。当然官方也提供了一些工具来方便开发者。 由于Dart的map和array的数据结构和json一样&#xff0c;所以在flutter中&#xff0c;变成了json string与Map&#xff0c;array之间的砖换。 &#x…

MYSQL数据库学习

数据库的基本概念 什么是数据库&#xff1f; 数据库&#xff08;database&#xff09;是用来组织 存储和管理数据的仓库。 当今世界是一个充满着数据的互联网世界&#xff0c;充斥着大量的数据。数据的来源有很多&#xff0c;比如出行记录 消费记录 浏览的网页 发送的信息等等…

《C++ Primer》 第十一章 关联容器

《C Primer》 第十一章 关联容器 11.1 使用关联容器 使用map: //统计每个单词在输入中出现的次数 map<string, size_t> word_count;//string到size_t的空map string word; while(cin>>word)word_count[word];//提取word的计数器并将其加1 for(const auto &w:…

Linux系统之部署Gitblit服务器

Linux系统之部署Gitblit服务器一、Gitblit介绍1.Gitblit简介2..Gitblit官网二、检查本地系统环境1.检查系统版本2.检查系统内核版本3.检查JDK版本三、下载Gitblit1.创建下载目录2.下载Gitblit软件包2.解压Gitblit软件包四、Gitblit的配置工作1.修改配置文件2.修改service-cento…

【小程序】如何实现一个可折叠的列表

作者刚接触小程序开发不久&#xff0c;打算用 CSDN 把学习过程中遇到的一些问题记录下来&#xff0c;都是一些浅显易懂的内容&#xff0c;希望对你也有所帮助。 如文章标题所示&#xff0c;作者要实现一个可折叠的列表&#xff0c;先来看一下页面效果&#xff1a; 这种展示方式…

ChatGPT API 低价上线,开发者可以人手一个了?

千呼万唤&#xff0c;ChatGPT API来了&#xff01; 不仅首发&#xff0c;价格居然还有惊喜&#xff0c;0.002美元/每1000 token&#xff0c;并将价格降低90%&#xff0c;直接打了1折。OpenAI官方还表示&#xff0c;gpt-3.5-turbo目前的版本代号是gpt-3.5-turbo-0301&#xff0…

【面试系列】equals和==的区别

问题&#xff1a;两个对象值相同(x.equals(y) true)&#xff0c;但是可能存在hashCode不同吗? 的定义 比较的是两个对象的内存地址&#xff0c;相等则意味着内存地址一样。 对象的equals方法 Object#equals public boolean equals(Object obj) {return (this obj);}Stri…

数据结构——基本概念

数据数据&#xff1a;是描述客观事物的符号&#xff0c;是计算机中可以操作的对象&#xff0c;是能被计算机识别&#xff0c;并输入给计算机处理的符号的集合。数据元素&#xff1a;是组成数据的&#xff0c;有一定意义的基本单位&#xff0c;在计算机中通常作为整体处理&#…

基于上下文分析的 Python 实时 API 推荐

原文来自微信公众号“编程语言Lab”&#xff1a;基于上下文分析的 Python 实时 API 推荐 搜索关注 “编程语言Lab”公众号&#xff08;HW-PLLab&#xff09;获取更多技术内容&#xff01; 欢迎加入 编程语言社区 SIG-程序分析 参与交流讨论&#xff08;加入方式&#xff1a;添加…

ICLR 2023 | LightGCL: 简单且高效的图对比学习推荐系统

论文题目&#xff1a;LightGCL: Simple Yet Effective Graph Contrastive Learning for Recommendation收录会议&#xff1a;ICLR 2023论文链接&#xff1a;https://arxiv.org/abs/2302.08191代码链接&#xff1a;https://github.com/HKUDS/LightGCL港大数据智能实验室 &#x…

billu_box靶场通关

billu靶场通关 靶机ip&#xff1a;192.168.112.134 信息收集 端口开放 80 目录扫描 images目录存在目录遍历 test.php(任意文件下载) add.php(文件上传) index.php(主页) head.php show.php c.php这个工具的字典不全&#xff0c;换py脚本重新扫多出了以下目录 phpmy in …

目标检测YOLOv5数据集怎么找?

完整的配置-标注-训练-识别在我这篇博客小白YOLOv5全流程-训练实现数字识别_yolov5数字识别_牛大了2022的博客-CSDN博客 模型部分剖析可以看我每周深度学习笔记部分。关于训练的数据集怎么搞很多人问过我&#xff0c;我在这篇文章给大家一点我的经验和建议。 数据集是什么 简…