从LeetCode 200‘岛屿数量’到蓝桥杯真题:手把手拆解DFS解题的完整思考链路

发布时间:2026/6/8 5:34:12

从LeetCode 200‘岛屿数量’到蓝桥杯真题:手把手拆解DFS解题的完整思考链路 从LeetCode 200到蓝桥杯真题DFS解题的思维拆解与实战迁移当你第一次面对LeetCode 200题岛屿数量时是否感觉无从下手或者虽然能写出代码却说不清为何这样设计本文将以这道经典题为切入点还原算法高手面对网格类问题的完整思考过程。不同于直接给出标准答案我们将重点拆解如何将实际问题转化为DFS可解的模型、递归函数设计的底层逻辑以及解题模式的通用迁移方法——这些正是蓝桥杯等算法竞赛考察的核心能力。1. 问题本质与建模从网格到图的思维跃迁面对一个由1和0组成的二维网格新手常犯的错误是仅将其视为二维数组。而高手的第一个思维转折点在于识别出网格本质上是一个隐式图结构。每个陆地格子(1)是图中的节点相邻上下左右的陆地之间存在隐式的边。# 网格示例图结构示意 grid [ [1, 1, 0, 0, 0], [1, 1, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, 1] ] # 对应图结构 # 节点(0,0)-(0,1)-(1,0)-(1,1)构成连通分量1 # 节点(2,2)自成连通分量2 # 节点(3,3)-(3,4)构成连通分量3这种转化之所以关键是因为它将问题归约到了连通分量计数这一经典图论问题。此时DFS的价值凸显出来——它能高效地探索连通区域。思考路径如下遍历每个网格单元发现未访问的1时启动DFS通过DFS标记所有相连的1为已访问岛屿数启动DFS的次数提示在竞赛中能否快速建立这种问题转化思维往往决定了能否在有限时间内解题。2. DFS设计的三层逻辑解剖2.1 感染机制避免重复访问的标记策略直接遍历会导致对同一岛屿的重复计数。高手常用的感染策略将访问过的1改为0本质上是隐式标记法相比显式维护visited数组这种就地修改的方法空间复杂度从O(MN)降至O(1)不考虑递归栈避免了额外的数据结构维护需要注意题目是否允许修改原数组def dfs(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 # 四向探索 dfs(grid, i1, j) dfs(grid, i-1, j) dfs(grid, i, j1) dfs(grid, i, j-1)2.2 递归参数设计的取舍艺术观察上述DFS函数参数设计体现了几个关键考量参数必要性替代方案选择理由grid必需全局变量避免全局变量提高函数独立性i,j必需栈维护递归天然调用栈更简洁无行列参数可选传入rows,colsPython中动态获取更灵活在蓝桥杯C实现中通常会传入行列数void dfs(vectorvectorchar grid, int i, int j, int rows, int cols) { if (i 0 || j 0 || i rows || j cols || grid[i][j] ! 1) return; grid[i][j] 0; dfs(grid, i1, j, rows, cols); // ...其他方向 }2.3 终止条件的完备性检查递归终止条件必须覆盖所有非法情况行索引越界i 0 或 i rows列索引越界j 0 或 j cols当前单元格非陆地grid[i][j] ! 1遗漏任何一项都会导致运行时错误。在竞赛中建议先用注释列出所有终止条件再编码。3. 复杂度分析与优化视角3.1 时间复杂度O(MN)的必然性每个单元格最多被访问两次主循环中的一次检查DFS过程中的一次访问因此时间复杂度严格为O(M×N)。这是理论下限因为必须检查每个单元格。3.2 空间复杂度递归深度的隐藏成本最坏情况下整个网格为1递归深度达O(MN)。对于大型网格如1000×1000这会导致栈溢出。此时可改用显式栈的迭代实现使用BFS替代空间复杂度相同但常数因子更优在允许情况下修改递归深度限制迭代DFS示例def dfs_iterative(grid, i, j): stack [(i, j)] while stack: x, y stack.pop() if 0 x len(grid) and 0 y len(grid[0]) and grid[x][y] 1: grid[x][y] 0 stack.append((x1, y)) stack.append((x-1, y)) stack.append((x, y1)) stack.append((x, y-1))4. 解题模式的通用迁移策略4.1 蓝桥杯真题中的变形应用以2021年蓝桥杯省赛草地灌溉题为例题目变形计算需要多少水源点才能灌溉所有草地G表示草地相邻草地可共享水源# 解法只需将岛屿数量中的1替换为G def count_sources(field): count 0 for i in range(len(field)): for j in range(len(field[0])): if field[i][j] G: count 1 dfs(field, i, j) return count4.2 连通性问题解题框架总结出通用模板主循环遍历每个元素触发条件当发现未处理的连通元素时扩散处理使用DFS/BFS标记所有连通元素计数规则触发次数即为连通分量数表格对比不同问题的参数调整问题类型元素判断条件连通规则标记方式岛屿数量grid[i][j]1四邻接1→0朋友圈(LeetCode 547)M[i][j]1矩阵对称visited数组围棋活子board[i][j]O四邻接边界判断临时标记4.3 调试技巧与常见陷阱在蓝桥杯实战中DFS实现常遇到无限递归终止条件不完整导致错误计数标记时机不当造成性能超时未及时剪枝或双重计数调试检查清单终止条件是否覆盖所有非法情况标记是在递归前还是递归后进行主循环是否跳过已处理元素递归方向是否完整四向/八向以被围绕的区域(LeetCode 130)为例特殊处理边界连通可提升效率def solve(board): if not board: return rows, cols len(board), len(board[0]) # 预处理边界上的O for i in range(rows): dfs(board, i, 0) dfs(board, i, cols-1) for j in range(cols): dfs(board, 0, j) dfs(board, rows-1, j) # 后续处理...这种预处理思维在竞赛中能显著优化时间复杂度。

相关新闻