硅基计划4.0 算法 FloodFill算法


文章目录


所谓FloodFill算法,本质上就是一种类似于洪水漫过的思想,即把题目中符合要求的区域都遍历到,而且内部是一个个的连通块

一、图像渲染

题目链接

这道题就是给你一个起始坐标,要求寻找和这个起始坐标相邻的连通块(只能是上下左右相邻)

我们可以从起始坐标出发,进行深度优先遍历,修改为题目中要求的目标值

并且把我们走过的路都标记上,避免无限递归(重复走)

对于特殊情况,比如题目传的是1,无需修改,直接返回即可

java 复制代码
class Solution {
    int height;
    int wide;
    //全局变量目标值
    int target;
    //中心区域的值
    int primitive;
    public int[][] floodFill(int[][] image, int sr, int sc, int color) {
        if(image[sr][sc] == color){
            //中心区域值已经和周围区域值相同
            //此时无需着色
            return image;
        }
        height = image.length;
        wide = image[0].length;
        primitive = image[sr][sc];
        target = color;
        dfs(image,sr,sc);
        return image;
    }

    //同理使用向量数组
    int [] x = {0,0,1,-1};
    int [] y = {1,-1,0,0};

    //posx表示第几行,posy表示第几列
    private void dfs(int [][] image,int posx,int posy){
        image[posx][posy] = target;
        for(int i = 0;i < 4;i++){
            int curX = posx+x[i];
            int curY = posy+y[i];
            if(curX >= 0 && curX < height && curY >= 0 && curY < wide && image[curX][curY] == primitive){
                dfs(image,curX,curY);
            }
        }
    }
}

二、岛屿数量

题目链接

这一题就是,我们扫描整个表,找到一个1的位置,从这个位置开始深度优先遍历,寻找一个个岛屿(连通块)

当我们扫描表的时候,如果这个地方是1,但是已经被扫描过了,就无需深度优先遍历

java 复制代码
class Solution {
    boolean [][] isUse;
    int height;
    int wide;
    //全局计数器,标记岛屿数量
    int count;
    public int numIslands(char[][] grid) {
        height = grid.length;
        wide = grid[0].length;
        isUse = new boolean[height][wide];
        for(int i = 0;i < height;i++){
            for(int j = 0;j < wide;j++){
                if(grid[i][j] == '1' && !isUse[i][j]){
                    //说明此时是一块新的岛屿
                    dfs(grid,i,j);
                    count++;
                }
            }
        }
        return count;
    }

    //同理使用向量数组
    int [] x = {0,0,1,-1};
    int [] y = {1,-1,0,0};

    //同理posx表示行,posy表示列
    private void dfs(char [][] grid,int posx,int posy){
        for(int i = 0;i < 4;i++){
            int curX = posx+x[i];
            int curY = posy+y[i];
            if(curX >= 0 && curX < height && curY >= 0 && curY < wide && grid[curX][curY] == '1' && !isUse[curX][curY]){
                //标记后,并递归
                isUse[curX][curY] = true;
                dfs(grid, curX, curY);
            }
        }
    }
}

三、最大岛屿面积

题目链接

这题和上一题思路一样,只不过我们要记录面积而已,只需要给一个变量统计面积,并且比较返回最大值就好

java 复制代码
class Solution {
    boolean [][] isUse;
    int height;
    int wide;
    //记录结果
    int maxArea;
    //接收每一次递归的值
    int dfsNum;
    public int maxAreaOfIsland(int[][] grid) {
        height = grid.length;
        wide = grid[0].length;
        isUse = new boolean[height][wide];
        //先去寻找第一个岛
        for(int i = 0;i < height;i++){
            for(int j = 0;j < wide;j++){
                if(grid[i][j] == 1 && !isUse[i][j]){
                    //本身就是一个面积,递归的时候传入1,不要忘记了
                    //并且也要标记为使用
                    dfsNum = 1;
                    isUse[i][j] = true;
                    dfs(grid,i,j,1);
                    maxArea = Math.max(dfsNum,maxArea);
                }
            }
        }
        return maxArea;
    }

    //同理使用向量数组
    int [] x = {0,0,1,-1};
    int [] y = {1,-1,0,0};

    //同理posx表示行,posy表示列
    private void dfs(int [][] grid,int posx,int posy,int pathCount){
        for(int i = 0;i < 4;i++){
            int curX = posx+x[i];
            int curY = posy+y[i];
            if(curX >= 0 && curX < height && curY >= 0 && curY < wide && grid[curX][curY] == 1 && !isUse[curX][curY]){
                pathCount++;
                dfsNum++;
                isUse[curX][curY] = true;
                dfs(grid, curX, curY, pathCount);
            }
        }
    }
}

四、被围绕的区域

题目链接

这一题难点就在于对于边缘情况的处理

如果你的想法是边缘就边缘呗,我到时候回溯的时候恢复现场即可

但是这样写太复杂了,你怎么知道它是边缘的区域呢

因此,我们采用反思路,既然边缘是特殊情况,那我优先把边缘的区域处理掉

剩下的内部不就是我们想要的区域吗

java 复制代码
class Solution {
    boolean [][] isUse;
    int height;
    int wide;
    public void solve(char[][] board) {
        height = board.length;
        wide = board[0].length;
        isUse = new boolean[height][wide];
        //先去扫描边界
        for(int i = 0;i < wide;i++){
            //第一行和最后一行扫描
            if(board[0][i] == 'O' && !isUse[0][i]){
                isUse[0][i] = true;
                dfs(board,0,i);
            }
            if(board[height-1][i] == 'O' && !isUse[height-1][i]){
                isUse[height-1][i] = true;
                dfs(board,height-1,i);
            }
        }
        for(int j = 0;j < height;j++){
            //第一列和最后一列扫描
            if(board[j][0] == 'O' && !isUse[j][0]){
                isUse[j][0] = true;
                dfs(board,j,0);
            }
            if(board[j][wide-1] == 'O' && !isUse[j][wide-1]){
                isUse[j][wide-1] = true;
                dfs(board,j,wide-1);
            }
        }
        //还原被修改的边界连通块
        for(int i = 0;i < height;i++){
            for(int j = 0;j < wide;j++){
                if(isUse[i][j]){
                    //说明是处理边界的连通块
                    board[i][j] = 'O';
                }
                if(board[i][j] == 'O' && !isUse[i][j]){
                    //说明是内部的连通块
                    board[i][j] = 'X';
                }
            }
        }
    }

    //同理使用向量数组
    int [] x = {1,-1,0,0};
    int [] y = {0,0,1,-1};

    //同理posx表示第几行,posy表示第几列
    private void dfs(char [][] board,int posx,int posy){
        for(int i = 0;i < 4;i++){
            int curX = posx+x[i];
            int curY = posy+y[i];
            if(curX >= 0 && curX < height && curY >= 0 && curY < wide && board[curX][curY] == 'O' && !isUse[curX][curY]){
                isUse[curX][curY] = true;
                board[curX][curY] = 'X';
                dfs(board, curX, curY);
            }
        }
    }
}

五、太平洋大西洋水流问题

题目链接

这一题起始还是和我们上一题一样的思路,既然我们判断从高处流向两个洋很难实现

那我们为什么不分别让两个洋的水往高处流呢?

这样我们两个洋水往高处流的相交区域,不就是题目要的可以流向两个洋的区域吗

java 复制代码
class Solution {
    boolean [][] isFloodPacific;
    boolean [][] isFloodAtlantic;
    int height;
    int wide;
    public List<List<Integer>> pacificAtlantic(int[][] heights) {
        //这题采用反思路,既然是流向两个洋
        //那我就可以逆着来,看看哪些边界的水可以倒着流向比较高的位置
        //到了最后两个洋重复的相交的区域就是我们想要的区域
        height = heights.length;
        wide = heights[0].length;
        isFloodPacific = new boolean[height][wide];
        isFloodAtlantic = new boolean[height][wide];
        //扫描边界,先处理太平洋
        for(int i = 0;i < height;i++){
            for(int j = 0;j < wide;j++){
                if(i == 0 && !isFloodPacific[i][j]){
                    isFloodPacific[i][j] = true;
                    dfs(heights,i,j,isFloodPacific);
                }
                if(j == 0 && !isFloodPacific[i][j]){
                    isFloodPacific[i][j] = true;
                    dfs(heights,i,j,isFloodPacific);
                }
            }
        }
        //扫描边界,再处理大西洋
        for(int i = 0;i < height;i++){
            for(int j = 0;j < wide;j++){
                if(i == height-1 && !isFloodAtlantic[i][j]){
                    isFloodAtlantic[i][j] = true;
                    dfs(heights,i,j,isFloodAtlantic);
                }
                if(j == wide-1 && !isFloodAtlantic[i][j]){
                    isFloodAtlantic[i][j] = true;
                    dfs(heights,i,j,isFloodAtlantic);
                }
            }
        }
        //统计结果
        List<List<Integer>> list = new ArrayList<>();
        //提取两个标记数组重复的部分
        for(int i = 0;i < height;i++){
            for(int j = 0;j < wide;j++){
                if(isFloodPacific[i][j] && isFloodAtlantic[i][j]){
                    List<Integer> tmp = new ArrayList<>();
                    tmp.add(i);
                    tmp.add(j);
                    list.add(tmp);
                }
            }
        }
        return list;
    }

    //同理使用向量数组
    int [] x = {0,0,-1,1};
    int [] y = {1,-1,0,0};

    //同理posx表示行,posy表示列
    private void dfs(int [][] heights,int posx,int posy,boolean [][] isFlood){
        //根据参数会自动辨别当前判断是哪个洋
        for(int i = 0;i < 4;i++){
            int curX = posx+x[i];
            int curY = posy+y[i];
            if(curX >= 0 && curX < height && curY >= 0 && curY < wide && !isFlood[curX][curY]){
                if(heights[curX][curY] >= heights[posx][posy]){
                    //代表此地可以往下流
                    isFlood[curX][curY] = true;
                    dfs(heights, curX, curY, isFlood);
                }
            }
        }
    }
}

六、扫雷游戏

题目链接

这题的意思就是,如果我们一开始就挖出地雷,游戏结束,把这个位置修改为X

如果没有一开始就踩雷,我们就去扫描周边区域

如果存在地雷,就要把地雷数量显示到当前位置 ,然后不能往周围再展开,要回溯到上一个区域(如果直接展开,那我就没必要排雷了)

我们之前写向量数组都是四个方向,现在变成八个方向了,这个需要注意

java 复制代码
class Solution {
    int height;
    int wide;
    public char[][] updateBoard(char[][] board, int[] click) {
        height = board.length;
        wide = board[0].length;
        //判断是否是直接点击了地雷
        int clickX = click[0];
        int clickY = click[1];
        if(board[clickX][clickY] == 'M'){
            //游戏直接结束
            board[clickX][clickY] = 'X';
            return board;
        }
        //接下来就不是直接点击了地雷的情况
        dfs(board,click[0],click[1]);
        return board;
    }

    //使用向量数组
    int [] x = {0,0,1,-1,1,1,-1,-1};
    int [] y = {1,-1,0,0,1,-1,1,-1};

    //同理posx表示行,posy表示列
    private void dfs(char [][] board,int posx,int posy){
        int countM = 0;
        for(int i = 0;i < 8;i++){
            int curX = posx+x[i];
            int curY = posy+y[i];
            //遍历周边区域,看看有没有地雷
            if(curX >= 0 && curX < height && curY >= 0 && curY < wide && board[curX][curY] == 'M'){
                countM++;
            }
        }
        if(countM > 0){
            //说明当前区域存在地雷,修改当前位置值之后,回溯
            board[posx][posy] = Character.forDigit(countM, 10);
            return;
        }
        //就递归地展开周边区域
        board[posx][posy] = 'B';
        for(int i = 0;i < 8;i++){
            int curX = posx+x[i];
            int curY = posy+y[i];
            if(curX >= 0 && curX < height && curY >= 0 && curY < wide && board[curX][curY] == 'E'){
                dfs(board, posx+x[i], posy+y[i]);
            }
        }
    }
}

七、衣橱整理

题目链接

这题的意思就是把坐标中各个数位提取出来,如果它们的和大于题目给的目标值,我们就不能进入这个区域

反之,如果数位的和小于目标值,则表示可以进入

比如对于坐标[35,37]区域,数位和3+5+3+7 = 18 <= target = 18,可以进入这个区域

反之比如坐标[35,38]区域,数位和3+5+3+8 = 19 > target = 18,不可以进入这个区域,进行回溯

java 复制代码
class Solution {
    int height;
    int wide;
    //统计结果
    int ret = 1;
    //全局变量目标值
    int target;
    //标记已经递归过的格子
    boolean [][] isUse;
    public int wardrobeFinishing(int m, int n, int cnt) {
        height = m;
        wide = n;
        target = cnt;
        isUse = new boolean[height][wide];
        isUse[0][0] = true;
        dfs(0,0);
        return ret;
    }

    //同理使用向量数组
    int [] x = {0,1};
    int [] y = {1,0};

    //同理posx表示行,posy表示列
    private void dfs(int posx,int posy){
        for(int i = 0;i < 2;i++){
            int curX = posx+x[i];
            int curY = posy+y[i];
            if(curX >= 0 && curX < height && curY >= 0 && curY < wide && !isUse[curX][curY]){
                //统计数位
                int tmpx = curX;
                int tmpy = curY;
                int count = 0;
                while(tmpx > 0){
                    count += tmpx % 10;
                    tmpx /= 10;
                }
                while(tmpy > 0){
                    count += tmpy % 10;
                    tmpy /= 10;
                }
                //进行判断
                if(count > target){
                    //不可以进行递归,寻找另一个方向
                    continue;
                }
                //到这里就说明可以进行递归
                isUse[curX][curY] = true;
                ret++;
                dfs(curX, curY);
            } 
        }
    }
}

感谢您的阅读,如果您有更好的建议,欢迎指出


END

相关推荐
阿拉斯攀登1 小时前
深入微服务配置中心:Nacos注册中心的实操细节
java·微服务·云原生·springcloud
f***24111 小时前
springboot系列--自动配置原理
java·spring boot·后端
菜鸟233号1 小时前
力扣347. 前k个高频元素 java实现
算法
一 乐1 小时前
水果销售|基于springboot + vue水果商城系统(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·后端
JIngJaneIL1 小时前
校园任务平台|校园社区系统|基于java+vue的校园悬赏任务平台系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·校园任务平台
三省同学1 小时前
SpringBoot 项目LOG_PATH_IS_UNDEFINED问题完整解决方案
java·spring boot·后端
阿蔹1 小时前
抓包工具Charles——介绍、篡改数据、弱网环境测试
java·自动化·抓包·charles
i***68321 小时前
【MyBatis】spring整合mybatis教程(详细易懂)
java·spring·mybatis
小马爱打代码1 小时前
Spring AI:ChatMemory 实现聊天记忆功能
java·人工智能·spring