算法奇妙屋(三十二)-DFS解决floodfill问题

文章目录

对于floodfill问题的解决, 我们在算法奇妙屋(十三)给了BFS的解决方式, 题目基本一样

一. 力扣 733. 图像渲染

1. 题目解析

给定一个坐标, 值为r, 从这个坐标开始向四个方位移动, 将值等于r的全部修改为color

2. 算法原理

这类题目相对简单, 这里直接给出

3. 代码

java 复制代码
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 >= 0 && x < m && y >= 0 && y < n && image[x][y] == r) {
                image[x][y] = c;
                dfs(image, x, y);
            }
        }
    }
}

二. 力扣 200. 岛屿数量

1. 题目解析

2. 算法原理

通过循环找到所有没被标记且数值为1的坐标, 然后丢入dfs中去标记, 每运行一次dfs就是一个岛屿

3. 代码

java 复制代码
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);
            }
        }
    }
}

三. 力扣 695. 岛屿的最大面积

1. 题目解析

2. 算法原理

1.函数头->发现重复子问题

我们需要知道每一个岛屿的任意一个坐标, 然后从当前坐标向四周延伸
1.全局变量: 标记数组vis, 记录最终结果ret, 记录每一个岛屿的面积的sum, 向量数组
2.参数: 原数组grid, 岛屿坐标i, j

2.函数体->宏观解决单个子问题

每次递归都代表有新的岛屿被发现, 需要向四个方位延伸来计算面积sum

3.递归的出口(起始点)

循环结束即代表递归结束

3. 代码

java 复制代码
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);
            }
        }
    }
}

四. 力扣 130. 被围绕的区域

1. 题目解析

这道题我们换个理解方式, 意思就是在边缘位置, 以及与边缘位置相连的区域 'O' 都不变, 而在中间被包围的不和边界 'O' 相连的都替换为 'X'

2. 算法原理

因为解决floodfill问题的思想基本一致, 所以这里只给出关键信息, 这道题主要运用正难则反的思想, 先将与边缘位置相邻的 'O' 都替换为一个标记字符 * , 然后遍历整个数组, 将剩余的 'O' 替换为 'X' , 标记字符 * 改为 'O'

3. 代码

java 复制代码
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);
            }
        }
        // 将所有边界区域'*'恢复, 并将'O'替换
        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);
            }
        }
    }
}

五. 力扣 417. 太平洋大西洋水流问题

1. 题目解析

这道题的最大难度就是读懂题目... 可以说力扣官方的翻译业务有待提升
题目的意思就是, 水往低处流, 边界的网格可以直接流入大海, 题目要求我们返回的是所有既可以流入太平洋又可以流入大西洋的坐标


2. 算法原理

这道题依旧采取正难则反的思想, 我们分别从太平洋和大西洋的边界出发, 让往高处流, 能从边界留到的高地, 也就可以反正可以从该高地流向边界, 最后太平洋和大西洋相交的点符合条件的位置(图中同时被红黑箭头指向的位置)

3. 代码

java 复制代码
class Solution {
    // po代表太平洋, ao代表大西洋
    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);
            }
        }
    }
}

六. 力扣 529. 扫雷游戏

1. 题目解析

题目字多还长, 但玩过扫雷的话还是很好理解这道题的


2. 题目解析

1. 重要的是让我们求的是, 给定一个坐标后, 这个坐标周围八个方位如果没有雷, 就可以揭开这些方位的格子, 同时这些被揭开的格子又可以向外扩张
2. 注意: 当给定的坐标周围八个方位只要有一个方向为雷, 就需要结束扩张(递归), 并在该位置标注上周围雷的个数
3. 我们需要两个八个方位的循环, 一个用来判断周围是否有雷, 并统计雷的个数, 一个用来向八方扩张

3. 代码

java 复制代码
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) {
        // 周围有地雷, 该位置修改为sum后, 直接结束递归
        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;
    }
}

七. 力扣 LCR 130. 衣橱整理

1. 题目解析

这道题相对来说比较好理解和简单, 这里我们只需要考虑两个方位的移动

2. 算法原理

根据指定的方位来进行dfs扩展

3. 代码

java 复制代码
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;
    }
}
相关推荐
m0_716667072 小时前
嵌入式C++驱动开发
开发语言·c++·算法
Lenyiin2 小时前
《LeetCode 顺序刷题》51 - 60
java·c++·python·算法·leetcode·深度优先·lenyiin
Sakinol#2 小时前
Leetcode Hot 100 —— 图论
算法·leetcode·图论
我怎么又饿了呀2 小时前
DataWhale—大模型的算法基础(环境的部署Anaconda)
人工智能·算法
ZZhYasuo2 小时前
冒泡排序1
java·算法·排序算法
重生之后端学习2 小时前
72. 编辑距离
数据结构·算法·leetcode·深度优先·图论
juleskk2 小时前
3.15 复试训练
算法
j_xxx404_2 小时前
力扣:525.连续数组和1314.矩阵区域和(二维前缀和)
算法·leetcode·矩阵
23.2 小时前
【Java】Arrays工具类——数组操作终极指南
java·算法·面试