优先搜索(DFS)实战

目录

一、DFS通用解题思路 

二、逐题拆解 

三、四题对比

四、总结:DFS解决矩阵问题的“万能模板” 


在算法解题中,矩阵连通性问题是高频考点,而深度优先搜索(DFS)是解决这类问题的核心工具之一。它通过“一条路走到底,不通再回头”的思路,能高效遍历矩阵中相邻的元素,进而完成标记、计数或替换等操作。本文将结合 LeetCode 130.被围绕的区域、695.岛屿的最大面积、200.岛屿数量 和 733.图像渲染 四题,拆解DFS在矩阵问题中的通用解题框架,并对比各题的差异点,帮助大家举一反三。
 


一、DFS通用解题思路
 


矩阵问题的核心是“相邻元素”的处理(通常指上下左右四个方向,特殊情况会包含对角线),DFS的作用就是从一个起始点出发,遍历所有与起始点连通的元素,并执行特定操作(如标记、修改值)。其通用步骤可归纳为:
 
1. 边界判断:遍历矩阵时,先检查当前元素是否越界(行号<0或>=矩阵行数,列号<0或>=矩阵列

数),若越界则直接返回,避免程序报错。
 

2. 条件筛选:判断当前元素是否符合“可遍历”条件(如是否为目标值、是否已被标记),若不符合

则返回,减少无效遍历。
 

3. 执行操作:对符合条件的当前元素执行操作(如标记为已访问、修改数值),防止后续重复遍

历。
 

4. 递归遍历:分别向当前元素的上、下、左、右四个方向递归调用DFS,继续遍历连通元素。
 
这四题均围绕该框架展开,差异仅在于“条件筛选”和“执行操作”的具体内容,接下来我们逐题分析。
 


二、逐题拆解
 


1. LeetCode 130.被围绕的区域——“边界连通元素的保护”
 
题目核心需求
 
给定一个  m x n  的矩阵,将所有被  'X'  围绕的  'O'  替换为  'X' ,与矩阵边界直接或间接连通的  'O'  需保留。
 
解题关键
 
被围绕的  'O'  的本质是“不与边界连通”,因此我们可以先标记出所有与边界连通的  'O' ,再将未被标记的  'O'  替换为  'X' ,最后还原被标记的  'O' 。
 
DFS实现步骤
 
1. 边界遍历:先遍历矩阵的四条边界(第一行、最后一行、第一列、最后一列),若遇到  'O' ,则以该元素为起点执行DFS。
2. DFS标记:在DFS中,将与边界连通的  'O'  标记为临时符号(如  '*' ),避免后续被误替换。
3. 矩阵更新:遍历整个矩阵,将剩余的  'O' (未与边界连通,即被围绕的)替换为  'X' ,将  '*'  还原为  'O' 。

 
关键代码片段

// DFS:标记与边界连通的'O'为'*'
void dfs(int x, int y, vector<vector<char>>& board) {// 1.边界判断 + 2.条件筛选(非'O'则返回)if (x < 0 || x >= board.size() || y < 0 || y >= board[0].size() || board[x][y] != 'O') {return;}// 3.执行操作:标记为'*'board[x][y] = '*';// 4.递归遍历四个方向dfs(x + 1, y, board);dfs(x - 1, y, board);dfs(x, y + 1, board);dfs(x, y - 1, board);
}void solve(vector<vector<char>>& board) {if (board.empty()) return;int m = board.size(), n = board[0].size();// 遍历边界,标记连通的'O'for (int i = 0; i < n; i++) {if (board[0][i] == 'O') dfs(0, i, board);       // 第一行if (board[m-1][i] == 'O') dfs(m-1, i, board);   // 最后一行}for (int i = 0; i < m; i++) {if (board[i][0] == 'O') dfs(i, 0, board);       // 第一列if (board[i][n-1] == 'O') dfs(i, n-1, board);   // 最后一列}// 更新矩阵for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {if (board[i][j] == 'O') board[i][j] = 'X';  // 未标记的'O'替换为'X'if (board[i][j] == '*') board[i][j] = 'O';  // 标记的'*'还原为'O'}}
}

2. LeetCode 200.岛屿数量——“连通区域的计数”
 
题目核心需求
 
给定一个  m x n  的二进制矩阵, '1'  代表陆地, '0'  代表水域,计算矩阵中岛屿的数量(岛屿是由相邻的  '1'  组成的连通区域,相邻指上下左右)。
 
解题关键
 
每遇到一个未被访问的  '1' ,就通过DFS遍历其所有连通的  '1'  并标记为已访问,每完成一次这样的DFS,岛屿数量加1。
 

DFS实现步骤
 
1. 矩阵遍历:遍历矩阵中的每个元素,若遇到  '1'  且未被标记,则岛屿数量加1,并执行DFS。
2. DFS标记:在DFS中,将当前的  '1'  标记为  '0' (或其他符号),表示已访问,避免重复计数。
3. 递归遍历:向四个方向递归,将连通的  '1'  全部标记为  '0' 。
 

关键代码片段
 
 

void dfs(int x, int y, vector<vector<char>>& grid) {// 1.边界判断 + 2.条件筛选(非'1'则返回)if (x < 0 || x >= grid.size() || y < 0 || y >= grid[0].size() || grid[x][y] != '1') {return;}// 3.执行操作:标记为'0'(已访问)grid[x][y] = '0';// 4.递归遍历四个方向dfs(x + 1, y, grid);dfs(x - 1, y, grid);dfs(x, y + 1, grid);dfs(x, y - 1, grid);
}int numIslands(vector<vector<char>>& grid) {if (grid.empty()) return 0;int m = grid.size(), n = grid[0].size();int count = 0;for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {if (grid[i][j] == '1') {  // 遇到未访问的陆地count++;dfs(i, j, grid);      // 标记整个岛屿}}}return count;
}

3. LeetCode 695.岛屿的最大面积——“连通区域的最大尺寸”
 
题目核心需求
 
给定一个  m x n  的二进制矩阵, 1  代表陆地, 0  代表水域,计算矩阵中最大岛屿的面积(岛屿面积为连通的  1  的个数)。
 
解题关键
 
与“岛屿数量”类似,但DFS需额外统计每个岛屿的面积(即连通的  1  的个数),并实时更新最大值。

 
DFS实现步骤
 
1. 矩阵遍历:遍历每个元素,若遇到  1  且未被标记,执行DFS并计算该岛屿的面积。
2. DFS计数:在DFS中,将当前  1  标记为  0 (已访问),并返回“1 + 四个方向连通区域的面积之和”,即当前岛屿的总面积。
3. 更新最大值:每次计算出一个岛屿的面积后,与当前最大值比较,保留较大值。

 
关键代码片段

int dfs(int x, int y, vector<vector<int>>& grid) {// 1.边界判断 + 2.条件筛选(非1则返回0,无面积)if (x < 0 || x >= grid.size() || y < 0 || y >= grid[0].size() || grid[x][y] != 1) {return 0;}// 3.执行操作:标记为0(已访问)grid[x][y] = 0;// 4.递归遍历四个方向,累加面积return 1 + dfs(x+1, y, grid) + dfs(x-1, y, grid) + dfs(x, y+1, grid) + dfs(x, y-1, grid);
}int maxAreaOfIsland(vector<vector<int>>& grid) {if (grid.empty()) return 0;int m = grid.size(), n = grid[0].size();int maxArea = 0;for (int i = 0; i < m; i++) {for (int j = 0; j < n; j++) {if (grid[i][j] == 1) {int area = dfs(i, j, grid);  // 计算当前岛屿面积maxArea = max(maxArea, area); // 更新最大值}}}return maxArea;
}

4. LeetCode 733.图像渲染——“指定区域的颜色替换”
 
题目核心需求
 
给定一个  m x n  的图像(由整数表示颜色),以及一个起始坐标  (sr, sc)  和新颜色  newColor ,将起始坐标所在的“连通区域”(颜色与起始坐标原颜色相同,相邻指上下左右)全部替换为新颜色。
 
解题关键
 
若起始坐标的原颜色与新颜色相同,直接返回图像(避免无限递归);否则通过DFS遍历所有连通的原颜色像素,替换为新颜色。
 
DFS实现步骤
 
1. 初始判断:记录起始坐标的原颜色  oldColor ,若  oldColor == newColor ,直接返回图像。
2. DFS替换:在DFS中,将当前像素的颜色替换为  newColor ,并向四个方向递归,仅替换颜色为  oldColor  的像素。

 
关键代码片段

void dfs(int x, int y, int oldColor, int newColor, vector<vector<int>>& image) {// 1.边界判断 + 2.条件筛选(非oldColor则返回)if (x < 0 || x >= image.size() || y < 0 || y >= image[0].size() || image[x][y] != oldColor) {return;}// 3.执行操作:替换为新颜色image[x][y] = newColor;// 4.递归遍历四个方向dfs(x+1, y, oldColor, newColor, image);dfs(x-1, y, oldColor, newColor, image);dfs(x, y+1, oldColor, newColor, image);dfs(x, y-1, oldColor, newColor, image);
}vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int newColor) {int oldColor = image[sr][sc];if (oldColor == newColor) return image; // 避免无限递归dfs(sr, sc, oldColor, newColor, image);return image;
}

三、四题对比

题目核心操作  条件筛选(DFS终止条件)特殊处理 
130.被围绕的区域标记边界连通的'O'非'O'或越界需还原标记(*→O)、替换未标记(O→X)
200.岛屿数量标记陆地并计数非'1'或越界每启动一次DFS,岛屿数+1 
695.最大岛屿面积标记陆地并计算面积非1或越界DFS返回面积,实时更新最大值 
733.图像渲染替换连通区域的颜色非原颜色或越界,原颜色==新颜色直接返回直接替换为新颜色,无需还原 

 

四、总结:DFS解决矩阵问题的“万能模板”
 

通过以上四题的分析,我们可以提炼出一个“矩阵DFS万能模板”,只需根据题目需求修改 条件筛选 和 执行操作 即可:

// 通用DFS函数:x,y为当前坐标,其他参数根据题目需求补充(如矩阵、目标值、新值等)
void dfs(int x, int y, 额外参数) {// 1. 边界判断if (x < 0 || x >= 矩阵行数 || y < 0 || y >= 矩阵列数) {return;}// 2. 条件筛选(是否为目标元素、是否已访问等)if (矩阵[x][y] 不满足条件) {return;}// 3. 执行操作(标记、修改值、计数等)对矩阵[x][y]执行具体操作;// 4. 递归遍历四个方向dfs(x + 1, y, 额外参数);dfs(x - 1, y, 额外参数);dfs(x, y + 1, 额外参数);dfs(x, y - 1, 额外参数);
}// 主函数:遍历矩阵,触发DFS
void mainFunction(矩阵类型& 矩阵, 其他参数) {if (矩阵为空) return;int 矩阵行数 = 矩阵.size(), 矩阵列数 = 矩阵[0].size();for (int i = 0; i < 矩阵行数; i++) {for (int j = 0; j < 矩阵列数; j++) {if (触发DFS的条件) { // 如遇到未访问的目标元素dfs(i, j, 额外参数);}}}// 若需后续处理(如还原标记、返回结果),在此处执行
}

矩阵连通性问题的本质是“找到并处理符合条件的连通区域”,DFS通过递归高效实现了这一过程。掌握上述模板后,无论题目要求是标记、计数还是替换,都能快速适配,真正做到“一题通,题题通”。

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

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

相关文章

门控MLP(Qwen3MLP)与稀疏混合专家(Qwen3MoeSparseMoeBlock)模块解析

Qwen3MLP Qwen3MLP是基于门控机制的MLP模块&#xff0c;采用了类似门控线性单元&#xff08;GLU&#xff09;的结构。它通过三个线性变换层&#xff08;gate_proj、up_proj和down_proj&#xff09;和SiLU激活函数&#xff0c;先将输入从隐藏维度扩展到中间维度&#xff0c;经过…

产线相机问题分析思路

现象&#xff1a;复现问题 原因&#xff1a;问题分析、溯源&#xff0c;定位根本原因&#xff1b; 方案&#xff1a;提出解决方案、规避措施 验证&#xff1a;导入、验证方案是否可行&#xff08;先小批量、再大批量&#xff09;&#xff1b;一. 现象产线反馈4pcs预览又脏污、划…

【开关电源篇】EMI输入电路-超简单解读

1. 输入电路主要包含哪些元件&#xff1f;滤波设计需遵循什么原则&#xff1f; 输入电路是电子设备&#xff08;如开关电源&#xff09;的“入口”&#xff0c;核心作用是抑制电磁干扰&#xff08;EMI&#xff09;、保护后级电路&#xff0c;其设计直接影响设备的稳定性和电磁…

胜券POS:打造智能移动终端,让零售智慧运营触手可及

零售企业运营中依然存在重重挑战&#xff1a;收银台前的长队消磨着顾客的耐心&#xff0c;仓库里的库存盘点不断侵蚀着员工的精力&#xff0c;导购培训的成本长期居高不下却收效甚微……面对这些痛点&#xff0c;零售企业或许都在等待一个破局的答案。百胜软件胜券POS&#xff…

(回溯/组合)Leetcode77组合+39组合总和+216组合总和III

为什么不能暴力&#xff0c;因为不知道要循环多少次&#xff0c;如果长度为n&#xff0c;难道要循环n次么&#xff0c;回溯的本质还是暴力&#xff0c;但是是可以知道多少层的暴力 之所以要pop是因为回溯相当于一个树形结构&#xff0c;要pop进行第二个分支 剪枝&#xff1a;…

07 下载配置很完善的yum软件源

文章目录前言ping 测试网络排查原因排查虚拟机的虚拟网络是否开启检查net8虚拟网络和Centos 7的ip地址是否在一个局域网点击虚拟网络编辑器点击更改设置记录net8的虚拟网络地址ip a记录Centos 7的ip地址比较net8和Centos 7的ip地址是否在一个网段解决问题问题解决办法修改net8的…

SpringBoot中添加健康检查服务

问题 今天需要给一个Spring工程添加健康检查。 pom.xml <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId> </dependency>application.yml management:endpoints:web:e…

AI工具深度测评与选型指南 - AI工具测评框架及方法论

目录引言&#xff1a;AI工具爆发期的机遇与挑战一、从AI模型到AI工具&#xff1a;核心认知与生态解析1.1 DeepSeek&#xff1a;快速出圈的国产大模型代表1.2 大模型的核心能力与类型划分1.2.1 大模型的三层能力与“双系统”类比1.2.2 生成模型与推理模型的核心差异1.3 AI工具与…

Spring Cloud Alibaba快速入门02-Nacos(中)

文章目录实现注册中心-服务发现模拟掉线远程调用1.订单和商品模块的接口商品服务订单服务2.抽取实体类3.订单服务拿到需要调用服务的ip和端口负载均衡步骤1步骤2步骤3步骤4面试题&#xff1a;注册中心宕机&#xff0c;远程调用还能成功吗&#xff1f;1、调用过;远程调用不在依赖…

【Python】数据可视化之热力图

热力图&#xff08;Heatmap&#xff09;是一种通过颜色深浅来展示数据分布、密度和强度等信息的可视化图表。它通过对色块着色来反映数据特征&#xff0c;使用户能够直观地理解数据模式&#xff0c;发现规律&#xff0c;并作出决策。 目录 基本原理 sns.heatmap 代码实现 基…

如何 正确使用 nrm 工具 管理镜像源

目录 nrm 是啥&#xff1f; nrm 的安装 查看你当前已有的镜像源 怎么切换到目标镜像源 添加镜像源 删除镜像源 测试镜像源速度 nrm 是啥&#xff1f; 镜像源&#xff1a;可以理解为&#xff0c;你访问或下载某jar包或依赖的仓库。 nrm&#xff08;Node Registry Manag…

关于对逾期提醒的定时任务~改进完善

Spring Boot 中实现到期提醒任务的定时Job详解在金融或借贷系统中&#xff0c;到期提醒是常见的功能需求。通过定时任务&#xff0c;可以定期扫描即将到期的借款记录&#xff0c;并生成或更新提醒信息。本文基于提供的三个JobHandler类&#xff08;FarExpireRemindJob、MidExpi…

springboot配置请求日志

springboot配置请求日志 一般情况下&#xff0c;接口请求都需要日志记录&#xff0c;Java springboot中的日志记录相对复杂一点 经过实践&#xff0c;以下方案可行&#xff0c;记录一下完整过程 一、创建日志数据模型 创建实体类&#xff0c;也就是日志文件中要记录的数据格式 …

Redis(50) Redis哨兵如何与客户端进行交互?

Redis 哨兵&#xff08;Sentinel&#xff09;不仅负责监控和管理 Redis 主从复制集群的高可用性&#xff0c;还需要与客户端进行有效的交互来实现故障转移后的透明连接切换。下面详细探讨 Redis 哨兵如何与客户端进行交互&#xff0c;并结合代码示例加以说明。 哨兵与客户端的交…

【.Net技术栈梳理】04-核心框架与运行时(线程处理)

文章目录1. 线程管理1.1 线程的核心概念&#xff1a;System.Threading.Thread1.2 现代线程管理&#xff1a;System.Threading.Tasks.Task 和 Task Parallel Library (TPL)1.3 状态管理和异常处理1.4 协调任务&#xff1a;async/await 模式2. 线程间通信2.1 共享内存与竞态条件2…

(JVM)四种垃圾回收算法

在 JVM 中&#xff0c;垃圾回收&#xff08;GC&#xff09;是核心机制之一。为了提升性能与内存利用率&#xff0c;JVM 采用了多种垃圾回收算法。本文总结了 四种常见的 GC 算法&#xff0c;并结合其优缺点与应用场景进行说明。1. 标记-清除&#xff08;Mark-Sweep&#xff09;…

论文阅读:VGGT Visual Geometry Grounded Transformer

论文阅读&#xff1a;VGGT: Visual Geometry Grounded Transformer 今天介绍一篇 CVPR 2025 的 best paper&#xff0c;这篇文章是牛津大学的 VGG 团队的工作&#xff0c;主要围绕着 3D 视觉中的各种任务&#xff0c;这篇文章提出了一种多任务统一的架构&#xff0c;实现一次输…

python编程:一文掌握pypiserver的详细使用

更多内容请见: python3案例和总结-专栏介绍和目录 文章目录 一、 pypiserver 概述 1.1 pypiserver是什么? 1.2 核心特性 1.3 典型应用场景 1.4 pypiserver优缺点 二、 安装与基本使用 2.1 安装 pypiserver 2.2 快速启动(最简模式) 2.3 使用私有服务器安装包 2.4 向私有服务…

Git reset 回退版本

- 第 121 篇 - Date: 2025 - 09 - 06 Author: 郑龙浩&#xff08;仟墨&#xff09; 文章目录Git reset 回退版本1 介绍三种命令区别3 验证三种的区别3 如果不小心git reset --hard将「工作区」和「暂存区」中的内容删除&#xff0c;刚才的记录找不到了&#xff0c;怎么办呢&…

ARM 基础(2)

ARM内核工作模式及其切换条件用户模式(User Mode, usr) 权限最低&#xff0c;运行普通应用程序。只能通过异常被动切换到其他模式。快速中断模式(FIQ Mode, fiq) 处理高速外设中断&#xff0c;专用寄存器减少上下文保存时间&#xff0c;响应周期约4个时钟周期。触发条件为FIQ中…