
本文详解在岛屿周长计算的 dfs 实现中,为何必须将 visited.add((i,j)) 放入 else 分支内——错误放置会导致边界边被漏计,从而返回错误的周长值(如 7 而非正确值 8)。
本文详解在岛屿周长计算的 dfs 实现中,为何必须将 visited.add((i,j)) 放入 else 分支内——错误放置会导致边界边被漏计,从而返回错误的周长值(如 7 而非正确值 8)。
在使用深度优先搜索(DFS)求解二维网格中岛屿周长问题时,一个看似细微的代码位置差异——visited.add((i, j)) 的放置位置——会直接导致结果错误。根本原因在于:周长的本质是统计所有「陆地格子与非陆地区域(水或边界)相邻的边数」,而非访问路径的去重;而 visited 的作用仅应限于避免重复遍历陆地格子,绝不应干扰边界贡献的计数逻辑。
错误写法:visited 放在判断前(导致漏计)
def dfs(i, j):
if (i, j) in visited:
return 0
visited.add((i, j)) # ❌ 错误:过早标记,连“无效位置”也纳入 visited
if i = ni or j >= nj or grid[i][j] == 0:
return 1 # 此处代表一条有效边界边
else:
return dfs(i-1, j) + dfs(i+1, j) + dfs(i, j-1) + dfs(i, j+1)
问题在于:当 DFS 从某个陆地格子(如 (0,1))向左探索 (-1,1)(越界)时,该坐标会被 visited.add((-1,1)) 记录。随后,当另一路径(如从 (1,1) 向上)再次尝试访问 (-1,1) 时,因已存在 visited 中,直接返回 0,本该贡献的第二条边界边被跳过。
以输入 [[0,1],[1,1]] 为例:
- 正确周长应为 8(形状类似“L”,外轮廓含 8 条单位边);
- 错误写法中,越界点 (-1,1)、(0,-1)、(2,1)、(1,2) 等可能被提前加入 visited,导致部分方向的边界贡献被抑制,最终返回 7。
正确写法:visited 仅标记有效陆地格子
def dfs(i, j):
if i = ni or j >= nj or grid[i][j] == 0:
return 1 # ✅ 边界/水域:立即返回 1,不修改 visited
if (i, j) in visited:
return 0 # ✅ 已访问陆地:跳过,避免循环
visited.add((i, j)) # ✅ 仅对新发现的陆地格子标记
return dfs(i-1, j) + dfs(i+1, j) + dfs(i, j-1) + dfs(i, j+1)
此时:
- 所有越界或水域坐标均不进入 visited 集合,每次遇到都如实返回 1;
- 每个陆地格子仅被访问一次,确保递归不陷入死循环;
- 每条“陆地→非陆地”的过渡边都被精确计为 1,累加即得真实周长。
关键原则总结
- visited 的语义必须清晰:它仅代表「已处理的陆地单元格」,绝不应包含边界坐标或水域坐标;
- 边界判定优先级最高:越界或值为 0 的情况应第一时间响应并返回 1,不参与任何状态更新;
- 调试建议:对小样例(如 [[0,1],[1,1]])手动绘制 DFS 调用树,标注每次 return 1 的触发位置,可直观验证是否所有 8 条边均被覆盖。
遵循这一逻辑,才能确保 DFS 在周长计算中既高效又准确——让算法真正“数清每一条海岸线”。











