Flood Fill泛洪算法
困难0好的,我将为你扩展这篇关于Flood Fill算法的文章,使其更详细、更易懂,适合中小学生阅读。
像“油漆桶”一样填色:Flood Fill 泛洪算法
你有没有用过画图软件里的“油漆桶”工具?点一下某个区域,所有连在一起的相同颜色就会变成你选的新颜色。这个神奇的功能背后,就用到了 Flood Fill(泛洪填充) 算法。它就像洪水一样,从你点击的那个格子开始,向四面八方蔓延,把和被点击格子颜色相同且相邻的所有格子都变成新颜色。
Flood Fill 算法在计算机里有很多用处,比如:
- 在游戏“扫雷”中点击空白格时,自动展开一大片区域;
- 统计地图上连在一起的湖泊或森林面积;
- 判断一张黑白图片中有多少个独立的连通区域。
下面,我们就来看看它是怎么实现的,以及用 Python 代码怎么写出来。
关键概念:连通、颜色、填充
1. 什么是“连通”?
想象一张方格纸,每个格子涂了颜色。两个格子如果上下左右(不是斜角)刚好挨着,我们就说它们是相邻的。如果一堆格子互相通过相邻关系连在一起(就像手拉手),就叫一个连通区域。
生活小例子:操场上,同学们手拉手围成一个圈。只有通过“手拉手”(相邻)才能组成一个团体。如果中间有个人没拉手(颜色不同),那这个团体就被分成了两段。
2. 填充规则
Flood Fill 只填充与起始点颜色相同且连通的所有格子。颜色不同的格子就像一堵墙,会挡住洪水,不让它流过去。
3. 两种实现方法:DFS 和 BFS
- DFS(深度优先搜索):从起点出发,一直往一个方向走到头,再回头走另一条路。就像在迷宫里走到底再折返。
- BFS(广度优先搜索):从起点出发,一层一层地向外扩张,就像石头扔进水里产生的涟漪。
两种方法都能完成填充,但各有特点:
- DFS 代码简单,但如果区域很大,递归可能太多,导致程序崩溃(栈溢出)。
- BFS 使用队列,不容易栈溢出,适合大区域。
新手常犯的错误
-
忘记检查新旧颜色是否相同
如果新颜色和旧颜色一样,递归会陷入死循环(因为染了等于没染,还会一直重复)。一定要在最开始判断并直接返回。 -
越界访问数组
检查格子是否在图片范围内时,条件写错(比如r >= rows写成r > rows),会导致程序报错。 -
颜色比较用错变量
在递归函数里,old_color可能会因为外层变量被修改而改变。最好在递归开始时把old_color作为参数传入,或者在函数内部用局部变量保存。 -
递归深度太大
Python 默认递归深度约 1000 层。如果图片很大(比如 1000x1000 的棋盘),DFS 可能失败。这时可以改用 BFS 或手动用栈实现。
完整代码示例
例1:DFS 实现(原示例保留并稍作注释)
def flood_fill_dfs(image, sr, sc, new_color):
"""
使用深度优先搜索(DFS)进行泛洪填充
image: 二维列表,表示图片上的颜色数字
sr, sc: 起始点击位置的行和列
new_color: 要填充的新颜色值
"""
rows = len(image) # 总行数
cols = len(image[0]) # 总列数
old_color = image[sr][sc] # 点击位置原来的颜色
# 如果新旧颜色相同,直接返回原图
if old_color == new_color:
return image
# 定义递归填充函数
def dfs(r, c):
# 越界或颜色不同就停止
if r < 0 or r >= rows or c < 0 or c >= cols:
return
if image[r][c] != old_color:
return
# 填充当前格子
image[r][c] = new_color
# 递归填充上下左右四个邻居
dfs(r + 1, c) # 下
dfs(r - 1, c) # 上
dfs(r, c + 1) # 右
dfs(r, c - 1) # 左
# 从起始位置开始填充
dfs(sr, sc)
return image
# ---------- 测试 ----------
canvas = [
[0, 0, 0, 0],
[0, 0, 1, 0],
[0, 1, 1, 0],
[0, 0, 0, 0]
]
result = flood_fill_dfs(canvas, 1, 1, 2)
for row in result:
print(row)
运行结果:
[2, 2, 0, 0]
[2, 2, 1, 0]
[0, 1, 1, 0]
[0, 0, 0, 0]
解释:
- 点击位置
(1,1)原色是0,DFS 把左上角连在一起的0都染成了2。 - 注意中间有两个
1(代表黑色),把上下两个白色区域隔开了,所以上面的0并没有被填充。这正是 Flood Fill 的特性:只填充连通的同色区域。
例2:BFS 实现(用队列)
from collections import deque
def flood_fill_bfs(image, sr, sc, new_color):
"""
使用广度优先搜索(BFS)进行泛洪填充
"""
rows = len(image)
cols = len(image[0])
old_color = image[sr][sc]
if old_color == new_color:
return image
# 定义一个方向数组:上下左右
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
# 使用队列保存待处理的坐标
queue = deque()
queue.append((sr, sc))
image[sr][sc] = new_color # 先染起始点
while queue:
r, c = queue.popleft()
# 检查四个邻居
for dr, dc in directions:
nr, nc = r + dr, c + dc
# 判断是否在框内,并且颜色等于旧颜色
if 0 <= nr < rows and 0 <= nc < cols and image[nr][nc] == old_color:
image[nr][nc] = new_color
queue.append((nr, nc))
return image
# ---------- 测试 ----------
canvas2 = [
[0, 0, 0, 0],
[0, 0, 1, 0],
[0, 1, 1, 0],
[0, 0, 0, 0]
]
result2 = flood_fill_bfs(canvas2, 1, 1, 2)
for row in result2:
print(row)
运行结果和上面一样。
注意:这里用了一个 directions 列表,让代码更清晰,也方便扩展到八方向(加上对角)。
生活中的应用举例
-
画图软件的油漆桶
你点一下白色区域,所有相邻的白色都变成红色。 -
扫雷游戏的展开
当你点开一个空格(周围没有雷),游戏会一下子展开一大片空白区域,用的就是 Flood Fill。 -
统计湖泊数量
在卫星地图上,把水的颜色标记为1,陆地标记为0。用 Flood Fill 可以数出有多少个独立的湖泊(连通区域)。每找一个新的1就填充成其他颜色,计数器加一。 -
模拟传染病传播
把感染者标记为特殊颜色,只要相邻的人接触就会被感染,Flood Fill 可以模拟疾病的扩散范围(但要注意,实际传染病传播更复杂哦)。
总结与相关指引
Flood Fill 是图遍历算法的一个经典应用。它帮助我们快速找到并处理一个连通区域。你可以把它看作是在一个“网格图”上做搜索:每个格子是一个节点,相邻格子之间有边(上下左右)。
相关知识点:
- 深度优先搜索(DFS):用递归或栈实现。适合小区域,代码简单。
- 广度优先搜索(BFS):用队列实现。适合大区域,不会栈溢出。
- 连通分量:统计图中有多少个独立的连通块,可以用 Flood Fill 或并查集(Union-Find)。
- 八方向连通:如果允许斜对角也连通,只需要在方向数组里加上
[(-1,-1), (-1,1), (1,-1), (1,1)]四个方向。
拓展练习:
- 修改代码,让你能一次填充对角相邻的格子(八方向)。
- 用 Flood Fill 计算图片中某个连通区域有多少个格子(只需加上一个计数器)。
- 尝试用 DFS 和 BFS 分别填一个 100x100 的纯色区域,看看哪个更快?
Flood Fill 是不是很简单?下次用画图软件时,你可以自豪地说:“我知道它是怎么工作的!”
例题精讲
在递归实现Flood Fill算法时,如果网格规模较大,容易导致以下哪种问题?
Flood Fill算法在填充过程中,如果目标颜色与原始颜色相同,算法应该直接返回,不做任何操作。
以下是用栈实现非递归深度优先Flood Fill的Python代码,请补全缺失的部分(用___表示)。
def flood_fill_stack(image, sr, sc, newColor):
oldColor = image[sr][sc]
if oldColor == newColor:
return
stack = [(sr, sc)]
while stack:
r, c = stack.pop()
if r < 0 or r >= len(image) or c < 0 or c >= len(image[0]) or image[r][c] != oldColor:
continue
image[r][c] = newColor
stack.append((r-1, c))
stack.append((r+1, c))
stack.append((r, c-1))
___ # 此处应添加对右邻居的入栈操作使用BFS(广度优先搜索)实现Flood Fill算法时,通常需要借助哪种数据结构来管理待处理的像素?
以下是用队列实现BFS Flood Fill的Python代码,但缺少了关键的条件判断,请补全(用___表示)。
from collections import deque
def flood_fill_bfs(image, sr, sc, newColor):
old = image[sr][sc]
if old == newColor:
return
q = deque()
q.append((sr, sc))
while q:
r, c = q.popleft()
if ___ or image[r][c] != old:
continue
image[r][c] = newColor
for dr, dc in [(-1,0), (1,0), (0,-1), (0,1)]:
nr, nc = r+dr, c+dc
q.append((nr, nc))