跳到主要内容 使用 DFS 解决 Flood Fill 类算法题 | 极客日志
Java java 算法
使用 DFS 解决 Flood Fill 类算法题 如何使用深度优先搜索(DFS)算法解决一系列经典的 Flood Fill 问题。内容涵盖七个 LeetCode 例题,包括图像渲染、岛屿数量、岛屿最大面积、被围绕的区域、太平洋大西洋水流问题、扫雷游戏以及衣橱整理。文章提供了每个问题的题目解析、算法原理说明及完整的 Java 代码实现,重点讲解了如何通过 DFS 遍历网格、标记访问状态以及处理不同方向的移动逻辑,帮助读者掌握此类图论问题的通用解法。
Pythonist 发布于 2026/3/28 更新于 2026/4/18 6 浏览
1. 题目解析
给定一个坐标,值为 r,从这个坐标开始向四个方位移动,将值等于 r 的全部修改为 color。
2. 算法原理
这类题目相对简单,核心在于深度优先搜索(DFS)遍历连通区域。
3. 代码
class Solution {
int c = 0 ;
int [] dx = {0 , 0 , -1 , 1 };
int [] dy = {-1 , 1 , 0 , 0 };
int m, n, r;
public int [][] floodFill(int [][] image, int sr, int sc, int color) {
c = color;
m = image.length;
n = image[0 ].length;
r = image[sr][sc];
image[sr][sc] = color;
dfs(image, sr, sc);
return image;
}
void dfs (int [][] image, int i, int j) {
if (image[i][j] == r) {
return ;
}
for (int k = 0 ; k < 4 ; k++) {
int x = i + dx[k];
int y = j + dy[k];
if (x >= && x < m && y >= && y < n && image[x][y] == r) {
image[x][y] = c;
dfs(image, x, y);
}
}
}
}
微信扫一扫,关注极客日志 微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
加密/解密文本 使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
0
0
1. 题目解析
2. 算法原理 通过循环找到所有没被标记且数值为 1 的坐标,然后丢入 DFS 中去标记,每运行一次 DFS 就是一个岛屿。
3. 代码 class Solution {
int ret = 0 , m, n;
boolean [][] vis;
public int numIslands (char [][] grid) {
m = grid.length;
n = grid[0 ].length;
vis = new boolean [m][n];
for (int i = 0 ; i < m; i++) {
for (int j = 0 ; j < n; j++) {
if (grid[i][j] == '1' && !vis[i][j]) {
vis[i][j] = true ;
dfs(grid, i, j);
ret++;
}
}
}
return ret;
}
int [] dx = {0 , 0 , -1 , 1 };
int [] dy = {-1 , 1 , 0 , 0 };
void dfs (char [][] grid, int i, int j) {
for (int k = 0 ; k < 4 ; k++) {
int x = i + dx[k];
int y = j + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y] == '1' ) {
vis[x][y] = true ;
dfs(grid, x, y);
}
}
}
}
1. 题目解析
2. 算法原理
函数头:发现重复子问题。需要知道每一个岛屿的任意一个坐标,然后从当前坐标向四周延伸。
全局变量:标记数组 vis,记录最终结果 ret,记录每一个岛屿的面积 sum,向量数组。
参数:原数组 grid,岛屿坐标 i, j。
函数体:宏观解决单个子问题。每次递归都代表有新的岛屿被发现,需要向四个方位延伸来计算面积 sum。
递归的出口:循环结束即代表递归结束。
3. 代码 class Solution {
int ret = 0 , sum = 0 , m, n;
boolean [][] vis;
int [] dx = {0 , 0 , -1 , 1 };
int [] dy = {-1 , 1 , 0 , 0 };
public int maxAreaOfIsland (int [][] grid) {
m = grid.length;
n = grid[0 ].length;
vis = new boolean [m][n];
for (int i = 0 ; i < m; i++) {
for (int j = 0 ; j < n; j++) {
if (grid[i][j] == 1 && !vis[i][j]) {
dfs(grid, i, j);
ret = Math.max(ret, sum);
sum = 0 ;
}
}
}
return ret;
}
void dfs (int [][] grid, int i, int j) {
vis[i][j] = true ;
sum++;
for (int k = 0 ; k < 4 ; k++) {
int x = i + dx[k];
int y = j + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y] == 1 ) {
dfs(grid, x, y);
}
}
}
}
1. 题目解析 在边缘位置,以及与边缘位置相连的区域 'O' 都不变,而在中间被包围的不和边界 'O' 相连的都替换为 'X'。
2. 算法原理 运用正难则反的思想,先将与边缘位置相邻的 'O' 都替换为一个标记字符 '',然后遍历整个数组,将剩余的 'O' 替换为 'X',标记字符 ' ' 改为 'O'。
3. 代码 class Solution {
boolean [][] vis;
int [] dx = {0 , 0 , -1 , 1 };
int [] dy = {-1 , 1 , 0 , 0 };
int m, n;
public void solve (char [][] board) {
m = board.length;
n = board[0 ].length;
vis = new boolean [m][n];
for (int i = 0 ; i < m; i++) {
if (board[i][0 ] == 'O' && !vis[i][0 ]) {
dfs(board, i, 0 );
}
if (board[i][n - 1 ] == 'O' && !vis[i][n - 1 ]) {
dfs(board, i, n - 1 );
}
}
for (int j = 0 ; j < n; j++) {
if (board[0 ][j] == 'O' && !vis[0 ][j]) {
dfs(board, 0 , j);
}
if (board[m - 1 ][j] == 'O' && !vis[m - 1 ][j]) {
dfs(board, m - 1 , j);
}
}
for (int i = 0 ; i < m; i++) {
for (int j = 0 ; j < n; j++) {
if (board[i][j] == 'O' ) {
board[i][j] = 'X' ;
}
if (board[i][j] == '*' ) {
board[i][j] = 'O' ;
}
}
}
}
void dfs (char [][] board, int i, int j) {
vis[i][j] = true ;
board[i][j] = '*' ;
for (int k = 0 ; k < 4 ; k++) {
int x = i + dx[k];
int y = j + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && board[x][y] == 'O' ) {
dfs(board, x, y);
}
}
}
}
1. 题目解析 水往低处流,边界的网格可以直接流入大海。题目要求返回的是所有既可以流入太平洋又可以流入大西洋的坐标。
2. 算法原理 采取正难则反的思想,分别从太平洋和大西洋的边界出发,让往高处流。能从边界流到的高地,也就可以反过来从该高地流向边界。最后太平洋和大西洋相交的点符合条件的位置。
3. 代码 class Solution {
boolean [][] po, ao;
int [] dx = {0 , 0 , -1 , 1 };
int [] dy = {-1 , 1 , 0 , 0 };
int m, n;
public List<List<Integer>> pacificAtlantic (int [][] heights) {
m = heights.length;
n = heights[0 ].length;
po = new boolean [m][n];
ao = new boolean [m][n];
for (int i = 0 ; i < m; i++) {
dfs(heights, i, 0 , po);
dfs(heights, i, n - 1 , ao);
}
for (int j = 0 ; j < n; j++) {
dfs(heights, 0 , j, po);
dfs(heights, m - 1 , j, ao);
}
List<List<Integer>> ret = new ArrayList <>();
for (int i = 0 ; i < m; i++) {
for (int j = 0 ; j < n; j++) {
if (po[i][j] && ao[i][j]) {
List<Integer> tmp = new ArrayList <>();
tmp.add(i);
tmp.add(j);
ret.add(tmp);
}
}
}
return ret;
}
void dfs (int [][] heights, int i, int j, boolean [][] vis) {
vis[i][j] = true ;
for (int k = 0 ; k < 4 ; k++) {
int x = i + dx[k];
int y = j + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && heights[x][y] >= heights[i][j]) {
dfs(heights, x, y, vis);
}
}
}
}
1. 题目解析 给定一个坐标后,这个坐标周围八个方位如果没有雷,就可以揭开这些方位的格子,同时这些被揭开的格子又可以向外扩张。
2. 算法原理
当给定的坐标周围八个方位只要有一个方向为雷,就需要结束扩张(递归),并在该位置标注上周围雷的个数。
我们需要两个八个方位的循环,一个用来判断周围是否有雷,并统计雷的个数,一个用来向八方扩张。
3. 代码 class Solution {
int [] dx = {0 , 0 , -1 , 1 , 1 , -1 , -1 , 1 };
int [] dy = {-1 , 1 , 0 , 0 , 1 , -1 , 1 , -1 };
int m, n, sum;
public char [][] updateBoard(char [][] board, int [] click) {
if (board[click[0 ]][click[1 ]] == 'M' ) {
board[click[0 ]][click[1 ]] = 'X' ;
return board;
}
m = board.length;
n = board[0 ].length;
dfs(board, click[0 ], click[1 ]);
return board;
}
void dfs (char [][] board, int i, int j) {
if (hasMine(board, i, j)) {
board[i][j] = (char ) (sum + '0' );
return ;
}
board[i][j] = 'B' ;
for (int k = 0 ; k < 8 ; k++) {
int x = i + dx[k];
int y = j + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'E' ) {
dfs(board, x, y);
}
}
}
boolean hasMine (char [][] board, int i, int j) {
boolean f = false ;
sum = 0 ;
for (int k = 0 ; k < 8 ; k++) {
int x = i + dx[k];
int y = j + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'M' ) {
sum++;
f = true ;
}
}
return f;
}
}
1. 题目解析 这道题相对来说比较好理解和简单,这里我们只需要考虑两个方位的移动。
2. 算法原理
3. 代码 class Solution {
boolean [][] vis;
int ret = 0 , m, n, c;
int [] dx = {0 , 1 };
int [] dy = {1 , 0 };
public int wardrobeFinishing (int mm, int nn, int cnt) {
m = mm;
n = nn;
c = cnt;
vis = new boolean [m][n];
dfs(0 , 0 );
return ret;
}
void dfs (int i, int j) {
vis[i][j] = true ;
ret++;
for (int k = 0 ; k < 2 ; k++) {
int x = i + dx[k];
int y = j + dy[k];
if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && digit(x, y) <= c) {
dfs(x, y);
}
}
}
int digit (int i, int j) {
int sum = 0 ;
while (i > 0 ) {
sum += i % 10 ;
i /= 10 ;
}
while (j > 0 ) {
sum += j % 10 ;
j /= 10 ;
}
return sum;
}
}