
本文详解如何正确实现经典的递归洪水填充算法,指出原代码中“逐轮全图扫描+同步更新”的根本性错误,并提供符合算法本质的深度优先递归方案及边界处理、状态管理等关键实践。
本文详解如何正确实现经典的递归洪水填充算法,指出原代码中“逐轮全图扫描+同步更新”的根本性错误,并提供符合算法本质的深度优先递归方案及边界处理、状态管理等关键实践。
洪水填充(Flood Fill)并非对整个网格进行多轮“扫描—标记—刷新”的模拟过程,而是一个以起始点为根、向连通区域扩散的深度优先遍历(DFS)问题。原实现存在三个核心缺陷:
- 语义错位:将 Flood Fill 误解为“每轮将所有邻接陆地转为水”,违背了其“从一个种子点出发、递归浸润连通区域”的定义;
- 竞态更新:floodStep() 中边遍历边修改 newArea,导致同一轮内多次覆盖(如 (r-1,c) 被设为 WATER 后,又作为 WATER 影响 (r-2,c)),造成非预期的跳跃式传播或漏填;
- 无终止保障:依赖 String 相等判断收敛,但因更新逻辑错误,可能陷入死循环或过早退出。
✅ 正确的递归 Flood Fill 应聚焦单一起始坐标(如首个 'L' 或指定入口),仅当该位置为可填充的陆地(LAND)时,才递归处理其四个正交邻居:
class RecursiveFloodFill implements FloodFill {
private static final char LAND = 'L';
private static final char WATER = 'W';
private char[][] grid;
@Override
public void flood(final String map, final FloodLogger logger) {
this.grid = stringToArea(map);
// 找到第一个陆地作为洪水起点(实际应用中可由参数指定)
for (int r = 0; r = grid.length ||
col = grid[row].length ||
grid[row][col] != LAND) {
return;
}
// 填充当前格子
grid[row][col] = WATER;
// 递归填充四个方向
floodFrom(row - 1, col); // 上
floodFrom(row + 1, col); // 下
floodFrom(row, col - 1); // 左
floodFrom(row, col + 1); // 右
}
// 辅助方法(保持原有逻辑)
private char[][] stringToArea(String map) {
String[] lines = map.split("\n");
char[][] area = new char[lines.length][];
for (int i = 0; i <p>? <strong>关键注意事项</strong>:</p>
- 避免重复访问:递归前必须校验 grid[row][col] == LAND,确保已填充的格子(WATER)不再进入;
- 边界安全第一:每次递归调用前严格检查行列索引,防止 ArrayIndexOutOfBoundsException;
- 不可逆填充:一旦设为 WATER,即视为已访问,天然避免环路与重复处理;
- 起始点明确性:真实场景中,起始坐标应由调用方传入(如 flood(String map, int startRow, int startCol, FloodLogger logger)),而非隐式搜索。
? 进阶提示:若需支持“多源洪水”(多个独立陆地区域同时起始),可先收集所有 LAND 坐标,再逐一调用 floodFrom();若需记录步数或可视化每层扩散,可在递归中引入深度参数并配合队列/栈实现迭代版 BFS。
综上,真正的 Flood Fill 是以点带面、深度优先、状态驱动的递归过程,而非全局扫描的“批量作业”。修正思维模型,才能写出简洁、健壮、符合算法本意的实现。










