LeetCode 733. 图像渲染
题目描述



题意分析
从给定起点开始,把与它上下左右连通、且原始颜色相同的所有像素改成新颜色,返回修改后的图像。连通路径上的每个格子都必须具有起点的原颜色,不能跨过其他颜色到达另一片同色区域。
对角线接触不算连通,图中其他同色但不连通的位置保持原样。新颜色与原颜色相同时,本来就不需要修改,可以直接返回。
解法:DFS 原地染色
核心思路
[!blue]
先保存起点的原始颜色
oldColor,整个遍历都使用这个固定值判断是否属于目标区域。深度优先搜索从起点出发,只继续进入界内且颜色仍为oldColor的格子,再沿四个方向扩散。进入一个合格格子后,先把它改成新颜色,再递归访问邻居。由于新旧颜色不同,已经染过的格子不再满足原色条件,新颜色同时充当已访问标记,防止两个相邻格子互相递归而无法结束。
这样不会染到区域外:每一次进入都来自同色区域中的四方向邻居;也不会漏掉区域内的格子,因为从起点可达的每条同色路径都会沿邻居递归被探索。每个合格格子实际染色一次,之后遇到它时立即返回。
新旧颜色相同时,上述标记方式失效,染色后仍满足原色条件,所以要在搜索前直接结束。这里的修改就是最终结果,递归返回时不需要恢复原颜色,与枚举路径的回溯不同。
解题步骤
- 保存起点原颜色;它已经等于目标颜色时,直接返回原图。
- 从起点调用 DFS,先排除越界位置,再排除颜色不等于原色的位置。
- 立即将当前格改成新色,标记它已经处理。
- 递归访问上、下、左、右邻居,沿同色连通区域继续扩散。
- 搜索结束后返回原图,此时目标区域已原地修改。
代码实现
class Solution {
// 必须先保存起点原始颜色。
public int[][] floodFill(int[][] image, int sr, int sc, int color) {
int oldColor = image[sr][sc];
// 同色时无法靠染色标记访问,直接返回
if (oldColor == color) {
return image;
}
dfs(image, sr, sc, oldColor, color);
return image;
}
private void dfs(int[][] image, int r, int c, int oldColor, int newColor) {
if (r < 0 || r >= image.length || c < 0 || c >= image[0].length) {
return;
}
if (image[r][c] != oldColor) {
return;
}
// 递归邻居前先染色,阻止相邻格互相回访
image[r][c] = newColor;
dfs(image, r + 1, c, oldColor, newColor);
dfs(image, r - 1, c, oldColor, newColor);
dfs(image, r, c + 1, oldColor, newColor);
dfs(image, r, c - 1, oldColor, newColor);
}
}
func floodFill(image [][]int, sr int, sc int, color int) [][]int {
// 必须先保存起点原始颜色。
oldColor := image[sr][sc]
// 同色时无法靠染色标记访问,直接返回
if oldColor == color {
return image
}
var dfs func(r, c int)
dfs = func(r, c int) {
if r < 0 || r >= len(image) || c < 0 || c >= len(image[0]) {
return
}
if image[r][c] != oldColor {
return
}
// 递归邻居前先染色,阻止相邻格互相回访
image[r][c] = color
dfs(r+1, c)
dfs(r-1, c)
dfs(r, c+1)
dfs(r, c-1)
}
dfs(sr, sc)
return image
}
复杂度分析
- 时间复杂度:最坏 $O(mn)$。目标连通区域的每格染色一次,每次最多检查四个邻居;若区域更小,实际处理量与区域大小成正比。
- 空间复杂度:最坏 $O(mn)$,递归路径可能覆盖整片区域。虽然不需要访问数组,调用栈仍占用空间。
关键点总结
[!green]
- 固定比较起点的原颜色,只遍历该颜色的四连通分量。
- 先染色再扩散,新色同时成为访问标记。
- 同色无需搜索,也避免标记无变化引起的循环访问。
- 不恢复颜色,递归完成后的改动就是要返回的结果。
易错点总结
[!yellow]
- 改写起点之后再从起点读取原色,整个搜索的判断基准会被换成新颜色。
- 递归邻居之后才染色,相邻未标记格子会反复调用对方。
- 新旧颜色相同仍然依赖染色标记,无法区分已经访问的位置。
- 允许斜向扩散,或把所有同色像素都改掉,扩大了起点四连通区域的范围。
- 递归返回时恢复原色,会撤销题目需要保留的渲染结果。
- 把原地修改误写成常数辅助空间,漏算可能达到区域大小的递归栈。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 200. 岛屿数量 | 中等 | 同样遍历四连通区域,本题只从一个起点出发并改色,原题统计全部陆地分量。 |
| 529. 扫雷游戏 | 中等 | 同样点击后扩散,本题所有同色格都可继续,扫雷只在周围无雷时继续展开。 |
| 695. 岛屿的最大面积 | 中等 | 用洪水填充标记网格连通分量;本题修改起点所属的同色分量,该题统计各分量面积并取最大。 |
| 1020. 飞地的数量 | 中等 | 用洪水填充标记网格连通分量;本题修改起点所属的同色分量,该题从边界排除可逃离的陆地。 |
| 1254. 统计封闭岛屿的数目 | 中等 | 用洪水填充标记网格连通分量;本题修改起点所属的同色分量,该题排除接触边界的零分量。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!