
本文详解为何在 dfs 判断子岛屿时,将多个递归调用写在同一 and 表达式中会导致逻辑错误,核心原因在于 python 的 and 运算符具有短路特性,会跳过后续递归调用,从而遗漏岛屿格子的访问与标记。
本文详解为何在 dfs 判断子岛屿时,将多个递归调用写在同一 and 表达式中会导致逻辑错误,核心原因在于 python 的 and 运算符具有短路特性,会跳过后续递归调用,从而遗漏岛屿格子的访问与标记。
在解决 LeetCode「统计子岛屿」(Count Sub Islands)这类二维网格 DFS 问题时,一个常见且隐蔽的错误源于对逻辑运算符 and 短路行为的忽视。题目要求:仅当 grid2 中某连通岛屿的所有陆地格子(1)在 grid1 中对应位置也均为陆地时,该岛屿才被视为“子岛屿”。因此,DFS 不仅需验证合法性,还必须完整遍历并标记整个 grid2 中的当前岛屿(即置 grid2[i][j] = 0),否则未访问的格子可能在后续循环中被重复计入,导致计数错误或逻辑判断失效。
关键问题出现在原始代码的这一行:
return dfs(i+1,j) and dfs(i-1,j) and dfs(i,j+1) and dfs(i,j-1)
表面看,这等价于依次调用四个方向的 DFS 并取逻辑与——但 Python 中 and 是短路运算符:一旦左侧子表达式返回 False,右侧所有表达式将完全不执行。例如,若 dfs(i+1,j) 返回 False(表示该方向存在非法格子,如 grid2 为 1 但 grid1 为 0),则 dfs(i-1,j)、dfs(i,j+1) 和 dfs(i,j-1) 根本不会被调用。结果是:当前岛屿中位于左、右、下的部分格子未被访问,grid2 中对应位置仍为 1,后续外层循环可能再次触发 DFS,造成重复计数或破坏岛屿完整性判断。
而拆分写法彻底规避了该风险:
one = dfs(i+1,j) # 强制执行 two = dfs(i-1,j) # 强制执行 three = dfs(i,j+1) # 强制执行 four = dfs(i,j-1) # 强制执行 return one and two and three and four
所有四个方向的 DFS 均被无条件调用,确保整个连通区域被完整遍历、标记(grid2[i][j] = 0),同时各方向的布尔结果再统一进行逻辑与判断。这才是符合题意的正确语义:先完成全部探索与状态更新,再综合判定是否为子岛屿。
✅ 正确实践建议:
- 在 DFS 中涉及副作用操作(如修改原数组、收集路径、更新全局状态)时,切勿将多个递归调用置于短路运算符(and / or)中;
- 若需组合多个布尔结果,务必先分别调用并保存,再进行逻辑运算;
- 可进一步优化为使用列表推导 + all(),语义更清晰:
directions = [(1,0), (-1,0), (0,1), (0,-1)] results = [dfs(i+di, j+dj) for di, dj in directions] return all(results)
总结:DFS 的正确性不仅取决于逻辑判断,更依赖于对整个连通分量的完整遍历与状态同步。忽视短路行为会导致隐性 bug,看似微小的语法差异,实则是算法健壮性的关键分水岭。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











