leetcode 题解仓库深度解析:深度优先遍历(DFS)从原理到实战模板

发布时间:2026/9/20 0:09:49

leetcode 题解仓库深度解析:深度优先遍历(DFS)从原理到实战模板 文档教程知识库【免费下载链接】leetcodeLeetCode Solutions: A Record of My Problem Solving Journey.( leetcode题解记录自己的leetcode解题之路。)项目地址https://gitcode.com/gh_mirrors/le/leetcode点击查看免费下载深度优先遍历Depth-First-SearchDFS是 leetcode 题解仓库中贯穿树、图、网格与回溯问题的核心算法之一本文以仓库内 thinkings/DFS.en.md 为骨架结合仓库收录的多道题解源码系统讲解 DFS 的定义、算法流程、通用模板、递归与迭代的取舍以及如何套用模板解决岛屿类连通区域问题。读完本文你将掌握一套可以直接复用的 DFS 模板并能看懂仓库中 200. 岛屿数量、130. 被围绕的区域 等经典题解的实现脉络。什么是深度优先遍历DFS深度优先搜索算法Depth-First-SearchDFS是一种用于遍历或搜索树或图的算法它沿着树的深度遍历节点尽可能深地搜索树的分支。当节点 v 的所有边都已被探寻过搜索将回溯到发现节点 v 的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止如果还存在未被发现的节点则选择其中一个作为源节点并重复以上过程直到所有节点都被访问。因为不借助启发式信息而只按既定规则穷举它属于盲目搜索。DFS 是图论中的经典算法利用它还可以为目标图生成相应的拓扑排序表借助拓扑排序表可以方便地解决很多图论问题例如最大路径问题等。正因如此因发明「深度优先搜索算法」约翰 · 霍普克洛夫特John Hopcroft与罗伯特 · 塔扬Robert Tarjan在 1986 年共同获得了计算机领域的最高奖——图灵奖。原文档还给出了一个量化参考截至 2020-02-21LeetCode 上深度优先遍历相关题目已有 129 道属于「超级大户」。对于树类题目基本都可以用 DFS 求解甚至 DFS 也可以实现广度优先遍历的效果。此外DFS 通常可以基于递归实现因此算法更简洁在性能要求很高的场合建议使用迭代否则尽量使用递归——不仅写起来简单快速还不容易出错。值得注意的是文档强调了一个关键区分DFS 的概念来自图论但搜索中的 DFS 与图论中的 DFS 略有区别——搜索中的 DFS 一般指通过递归函数实现暴力枚举这与后文回溯专题的定位一脉相承。DFS 的算法流程原文档给出了 DFS 最经典的六步流程其核心载体是一个栈stack首先将根节点放入stack中。从stack中取出第一个节点并检验它是否为目标。如果找到目标则结束搜寻并回传结果否则将它某一个尚未检验过的直接子节点加入stack中。重复步骤 2。如果不存在未检测过的直接子节点将上一级节点加入stack中重复步骤 2。重复步骤 4。若stack为空表示整张图都检查过了——即图中没有欲搜寻的目标结束搜寻并回传「找不到目标」。这里的 stack 可以理解为自实现的栈也可以理解为调用栈递归时由系统隐式维护。正因为「栈」既可以显式声明、也可以由递归函数的调用栈隐式提供所以 DFS 在工程上天然有两种实现形态显式栈的迭代写法与递归写法。递归写法因代码量与调用栈语义与算法描述一一对应成为绝大多数题解的首选。DFS 通用算法模板原文档给出了一段简洁的 JavaScript 模板这是 DFS 的「最小骨架」——用visited记录已搜索状态用递归进行状态转移const visited {} function dfs(i) { if (满足特定条件{ // 返回结果 or 退出搜索空间 } visited[i] true // 将当前状态标为已搜索 for (根据i能到达的下个状态j) { if (!visited[j]) { // 如果状态j没有被搜索过 dfs(j) } } }从模板可以看出 DFS 的三个关键要素终止条件满足特定条件如越界、找到目标、状态非法时立即返回避免无限递归状态标记visited[i] true防止重复访问同一个状态避免死循环状态扩展for循环枚举从当前状态 i 能到达的所有下一状态 j对未访问过的 j 递归调用。二维网格上的 DFS 模板以 200. 岛屿数量为例当状态是二维网格的坐标时模板扩展为「四方向扩散」的形式。仓库题解 200. 岛屿数量 中给出了 C、Java、JavaScript、Python3 四种实现以 Python 版为例class Solution: def numIslands(self, grid: List[List[str]]) - int: if not grid: return 0 count 0 for i in range(len(grid)): for j in range(len(grid[0])): if grid[i][j] 1: self.dfs(grid, i, j) count 1 return count def dfs(self, grid, i, j): if i 0 or j 0 or i len(grid) or j len(grid[0]) or grid[i][j] ! 1: return grid[i][j] 0 self.dfs(grid, i 1, j) self.dfs(grid, i - 1, j) self.dfs(grid, i, j 1) self.dfs(grid, i, j - 1)这道题的关键技巧在于将已访问的元素直接置为 0原地标记省去 visited 数组的空间开销。因为数字 0 本身不会被递归处理置 0 就等价于「已访问」。这是二维数组 DFS 模板的常考优化点题解中明确总结为两条关键点二维数组 DFS 解题模板将已经访问的元素置为 0省去 visited 的空间开销。其复杂度为时间 $O(m * n)$、空间 $O(m * n)$递归调用栈深度m、n 分别为网格行数与列数。两种 visited 标记方式关于「是否需要 visited」仓库专题 小岛问题 给出了更完整的对比既可以用seen集合记录访问状态搜索结束后可撤销标记也可以原地修改board[i][j]作为访问标记。后者省去额外空间是小岛类题目非常常用的技巧务必熟练掌握def dfs(i, j): if i 越界 or j 越界: return if board[i][j] -1: return temp board[i][j] board[i][j] -1 # 标记为访问过 dfs(i 1, j) # 上 dfs(i - 1, j) # 下 dfs(i, j 1) # 右 dfs(i, j - 1) # 左 board[i][j] temp # 撤销标记递归 vs 迭代的取舍原文档给出的实践建议非常明确性能要求很高的场合用迭代否则尽量用递归。递归的优势在于代码与算法描述同构、书写快、不易出错迭代的优势在于显式控制栈、可避免深递归导致调用栈溢出在网格尺寸很大、递归深度等于状态数量时尤为重要。仓库在树遍历场景下对这一取舍做了更细化的展开。在 二叉树的遍历算法 中可以看到前序、中序、后序遍历都属于 DFS且「DFS 都可以使用栈来简化操作」树本身是递归的数据结构因此递归和栈是 DFS 的两个关键点文中进一步给出了「双色标记法」——用颜色标记节点状态新节点为白色、已访问为灰色以迭代方式模拟递归其写法与递归非常接近但避免了系统调用栈的开销是一种「用迭代模拟递归」的实用技巧若要求 $O(1)$ 空间遍历二叉树则可了解 Morris 遍历不使用递归也不借助栈。DFS 与回溯、BFS 的关系原文档特别建议深度优先遍历可以结合回溯专题一起学习两者配合效果更佳。仓库中的 回溯 专题正是这样定位的回溯是 DFS 中的一种技巧采用试错的思想分步解决问题当发现当前分步答案无法得到正确解答时取消上一步甚至上几步的计算再尝试其他可能。回溯的本质是穷举所有可能尽管可通过剪枝去除不可能是答案的分支本质上仍是暴力枚举。回溯模板与 DFS 模板的差别只在三处进入递归前dosomething(i)、递归后undo(i)恢复状态撤销这正是「回溯」二字的技术含义——因为结果通常记录在回溯树的路径上若不撤销回溯后状态不正确会导致结果差异const visited {} function dfs(i) { if (满足特定条件{ // 返回结果 or 退出搜索空间 } visited[i] true dosomething(i) // 对i做一些操作 for (根据i能到达的下个状态j) { if (!visited[j]) { dfs(j) } } undo(i) // 恢复i }如果每次递归都拷贝一份数据那么就不需要撤销状态代价是空间复杂度上升。此外回溯的另一个重要考点是剪枝——避免根本不可能是答案的递归可显著减少时间开销。至于 DFS 与 BFS 的关系原文档指出基于 DFS 也可以实现广度优先遍历并不一定说 DFS 做不了 BFS 的事情二者各有应用场景例如 LeetCode 301 号与 609 号问题分别对应 DFS 与 BFS 的典型用法。实战把模板套进连通区域问题DFS 在网格题中最典型的应用是「求连通区域」仓库专题 小岛问题 将其总结为从一个或多个入口 DFS向四个方向延伸。这一套路直接体现在仓库收录的多道题解中。200. 岛屿数量模板题题目要求计算二维网格中由 1 组成的岛屿连通区域数量解题思路即前文模板遍历网格遇到 1 就进入 DFS 将其所在的整个连通块标记为 0计数加一。仓库题解见 200. 岛屿数量该题在题解中被标注为「模板题-岛屿」是掌握二维 DFS 的最佳起点。130. 被围绕的区域入口选择 逆向思考题解 130. 被围绕的区域 展示了 DFS 的另一种玩法题目要求把被 X 完全围绕的 O 改为 X但边界上的 O 及其连通的 O 不能被修改。逆向思考后问题转化为「标记边界 O 及其连通块」的连通区域问题先从四条边界出发做 DFS把所有与边界相连的 O 标记为 A原地 mutate节省空间再遍历全图剩下的 O 都是被围绕的改为 XA 则还原为 O。题解给出了 JS、Python3、C 三种实现并明确其「解题模板和 200 题一样」时间复杂度 $O(row * col)$、空间复杂度 $O(row * col)$。这提示了一个通用策略当题目对「哪些算、哪些不算」有边界特判时可从边界入口反向 DFS 标记。更多延伸仓库 小岛问题 还收录了岛屿周长463类题目的技巧DFS 遇到陆地加 4并判断左侧与上方是否也为陆地以减去重复计算的 2 条边以及将 /、 斜杠网格放大为 3 倍网格后转化为求连通区域个数的思路对应 959. 由斜杠划分区域。另外连通分量类问题也可用并查集解决可参考 547. 省份数量 的find/union/connected模板。题目推荐与学习路线原文档在文末给出了近期总结的 DFS 题目清单并说明会持续更新。结合仓库实际收录情况推荐按以下顺序练习200. 岛屿数量中等DFS 二维网格模板题建议先吃透695. 岛屿的最大面积中等在 DFS 过程中累计面积是 200 题的直接变体979. 在二叉树中分配硬币中等将 DFS 应用于二叉树体会树形 DFS 的返回值设计进阶参考130. 被围绕的区域、小岛问题 中列出的 463 题、1162 题等。学习路径建议先按本文模板独立写出 200 题再对照 200. 岛屿数量 的多语言实现检查边界处理随后用 小岛问题 的「原地标记」技巧重写一遍体会空间优化最后结合 回溯 与 二叉树的遍历算法 两个专题理解 DFS 在状态搜索与树遍历中的不同形态。总结DFS 是图与树上的盲目搜索算法沿着深度优先扩展配合栈显式或调用栈回溯直到遍历完所有可达节点一套模板打天下visited标记 递归状态扩展的模板可迁移到网格四方向、树形结构与状态空间搜索递归优先、迭代兜底普通场景用递归求简洁性能敏感或深度过大时改用显式栈迭代与回溯强关联回溯 DFS 状态撤销undo 剪枝建议两个专题一起学习岛屿类问题是 DFS 的最佳入门场景连通区域求解 原地标记优化是最高频的考察组合。本仓库的 thinkings/DFS.en.md 与中文版 thinkings/DFS.md 提供了完整的理论与模板配合 200. 岛屿数量、130. 被围绕的区域、小岛问题、回溯 等实战资料即可构成一条「原理 → 模板 → 变体 → 专题」的完整 DFS 学习链路。赞分享文档教程知识库【免费下载链接】leetcodeLeetCode Solutions: A Record of My Problem Solving Journey.( leetcode题解记录自己的leetcode解题之路。)项目地址https://gitcode.com/gh_mirrors/le/leetcode点击查看免费下载相关推荐LeetCode 深度优先遍历DFS专题从算法流程到小岛问题实战模板LeetCode 深度优先遍历DFS专题从算法流程到小岛问题实战模板 深度优先遍历Depth First SearchDFS是 leetcode 题文档教程知识库hello-algo 图遍历全解BFS 广度优先与 DFS 深度优先遍历的算法原理、代码实现与复杂度分析hello algo 图遍历全解BFS 广度优先与 DFS 深度优先遍历的算法原理、代码实现与复杂度分析 图的遍历Graph Traversal是图论算法教程文档示例工程教育Sunshine 游戏串流实操免费低延迟4 步从安装到跑通Sunshine 游戏串流实操免费低延迟4 步从安装到跑通 Sunshine 是一个开源自托管游戏串流服务器作为 Moonlight 客户端的主机端把音视频后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/9/20 0:04:49

BrewUI:给Homebrew套上图形界面,让macOS软件包管理更简单

先说个挺常见的场景:你刚拿到一台新 Mac,想装 Node、Python、Git 这些开发工具,打开终端准备用 Homebrew 一把梭,结果安装脚本跑了一半卡住,终端里刷出密密麻麻的报错信息。旁边还有一位做设计的朋友问:“你…

2026/9/20 0:04:49

BrewUI:给Homebrew套上图形界面,打造macOS包管理新体验

老实说,第一次看到BrewUI这名字的时候,我下意识以为又是某个咖啡机控制面板的 DIY 项目。毕竟 Brew 这词,在手工咖啡圈子里太常见了。结果点进去一查,发现完全不是那么回事——它是冲着 macOS 上那个大名鼎鼎的Homebrew来的&#…

2026/9/20 0:59:51

Flutter流式CSV处理库serial_csv鸿蒙适配实战

1. Flutter 三方库 serial_csv 的鸿蒙化适配实战指南在移动应用开发领域,数据交换格式的选择往往直接影响着应用性能和用户体验。CSV(Comma-Separated Values)作为一种轻量级、通用性强的文本格式,在金融报表、工业数据采集等场景…

2026/9/20 0:59:51

数据要素价值释放的技术架构与实践路径

1. 数据要素价值释放的底层逻辑数据作为新型生产要素,其价值实现路径与传统要素存在本质差异。在传统工业经济中,土地、劳动力、资本等要素的价值呈现线性叠加特征,而数据要素的价值实现则表现出明显的网络效应和乘数效应——单一数据经过清洗…

2026/9/20 0:59:51

Open-Code-Review:Agent驱动的多语言行级代码评审范式

1. 这不是传统Code Review,而是一次开发协作范式的迁移“open-code-review”这个词最近在GitHub趋势榜和开发者社区讨论帖里频繁出现,但它绝不是“把代码评审流程搬到开源平台”这么简单。我从去年底开始在三个中型团队里落地这个实践,从最初…

2026/9/20 0:59:51

moltbot智能对话机器人Linux本地部署与优化指南

1. 项目概述moltbot作为一款新兴的智能对话机器人,其Linux本地部署方案正在技术社区引发广泛讨论。不同于云端部署方案,本地化安装不仅能保障数据隐私,还能根据硬件配置进行深度优化。最近我在一台Ubuntu 22.04 LTS服务器上完整走通了部署流程…

2026/9/20 0:54:51

GitHub Copilot 退订转 Cursor:Base URL 填 TaoToken

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/20 0:04:49

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/20 0:04:49

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/20 0:04:49

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/20 0:04:49

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/18 14:13:03

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/18 14:13:02

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/18 14:13:02

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码