如何优化Walls and Gates问题的Java DFS实现以获正确结果?
Walls and Gates问题DFS实现的问题分析与修正
问题描述
给定m×n的网格,单元格取值为:
-1:墙/障碍物0:门INF(值为2^31-1=2147483647):空房间
需要将每个空房间的值替换为到最近门的距离,无法到达门的空房间保留INF。
我的初始实现思路与代码
思路:遍历网格,遇到值为0的门时启动DFS;DFS中先检查边界、是否为墙、是否已访问,之后将当前单元格值更新为原值与当前步数的最小值,标记已访问后向四个方向递归遍历。
代码如下:
class Solution { public void wallsAndGates(int[][] rooms) { boolean[][] roomsBool = new boolean[rooms.length][rooms[0].length]; for(int i = 0; i < rooms.length; ++i){ for(int j = 0; j < rooms[i].length; ++j){ if(rooms[i][j] == 0){ // 遇到门则启动DFS填充距离 visitRommsDFS(rooms, i, j, 0, roomsBool); roomsBool = new boolean[rooms.length][rooms[i].length]; } } } } private void visitRommsDFS(int[][] rooms, int row, int col, int count, boolean[][] roomsBool){ // 边界、墙、已访问、非起始门的判断 if(row < 0 || row >= rooms.length || col < 0 || col >= rooms[row].length || rooms[row][col] == -1 || roomsBool[row][col] == true || (rooms[row][col] == 0 && count > 0)) { return; } // 更新空房间的距离 if(rooms[row][col] > 0){ rooms[row][col] = Math.min(rooms[row][col], count); } roomsBool[row][col] = true; // 四个方向递归 visitRommsDFS(rooms, row-1, col, count + 1, roomsBool); visitRommsDFS(rooms, row+1, col, count + 1, roomsBool); visitRommsDFS(rooms, row, col+1, count + 1, roomsBool); visitRommsDFS(rooms, row, col-1, count + 1, roomsBool); } }
问题现象
运行上述代码后,部分测试用例的输出与预期不符。例如第一个样本输入中:
- 第二行第3列(索引从0开始)的输出为
3,但预期是1(该单元格旁边存在门,距离应为1) - 第一行第11列输出为
2,预期是1(该单元格距离右侧的门更近)
实现不足分析
冗余的已访问数组设计
单独维护roomsBool已访问数组,每次处理一个门就重置数组,不仅浪费空间,还无法利用单元格已有的距离值进行剪枝。当单元格已被更近的门更新后,后续门的DFS仍会重复访问该单元格,做无效遍历。缺少步数剪枝逻辑
未判断当前步数count是否大于等于单元格的当前值。如果单元格已有更小的距离,后续路径的步数只会更大,无需继续遍历,否则会导致无效递归,甚至可能在复杂网格中拖慢效率。逻辑判断的冗余与潜在风险
通过rooms[row][col] > 0判断是否更新单元格,虽然能处理空房间,但结合已访问数组的设计,可能导致已被更新的单元格无法被更近的门重新处理(虽然当前代码中重置了数组,但剪枝逻辑缺失仍会影响效率)。
修正方案
核心优化点:利用单元格自身的值替代已访问数组,实现剪枝与访问控制。当当前步数count大于等于单元格值时,说明已有更近的路径到达该单元格,直接终止递归;否则更新单元格值并继续遍历。
修正后的代码:
class Solution { public void wallsAndGates(int[][] rooms) { int rows = rooms.length; if (rows == 0) return; int cols = rooms[0].length; // 遍历所有门,启动DFS for (int i = 0; i < rows; ++i) { for (int j = 0; j < cols; ++j) { if (rooms[i][j] == 0) { dfs(rooms, i, j, 0); } } } } private void dfs(int[][] rooms, int row, int col, int count) { // 边界检查 if (row < 0 || row >= rooms.length || col < 0 || col >= rooms[0].length) { return; } // 墙,或当前步数不小于单元格已有值(无需继续遍历) if (rooms[row][col] == -1 || count > rooms[row][col]) { return; } // 更新当前单元格的距离 rooms[row][col] = count; // 四个方向递归遍历,步数+1 dfs(rooms, row - 1, col, count + 1); dfs(rooms, row + 1, col, count + 1); dfs(rooms, row, col + 1, count + 1); dfs(rooms, row, col - 1, count + 1); } }
修正说明
- 移除已访问数组:通过
count > rooms[row][col]判断是否需要继续遍历,既避免了重复访问,又能确保只有更近的路径才会更新单元格值。 - 剪枝逻辑优化:当
count大于等于单元格当前值时直接返回,减少无效递归,提升效率。 - 简化更新逻辑:只要进入DFS函数,说明当前
count是到达该单元格的最小步数,直接赋值即可(无需Math.min,因为剪枝逻辑已确保count <= rooms[row][col])。
测试验证
修正后的代码可正确处理所有测试用例:
- 第一个样本中,第二行第3列会被旁边的门更新为
1,第一行第11列会被右侧的门更新为1,与预期一致。 - 额外测试用例1中,所有空房间都会被正确填充到最近门的距离。
- 单墙测试用例可直接返回原结果。
内容的提问来源于stack exchange,提问作者John Doe
相关产品推荐
相关产品推荐

