FloodFill算法---DFS

目录

floodfill算法概念:

算法模板套路: 

例题1:图像渲染

例题2:岛屿数量

例题3:岛屿的最大面积

例题4:被围绕的区域


floodfill算法概念:

floodfill算法是一种常用的图像处理算法,用于填充连通区域。它从指定的种子点开始,将相邻的像素点按照某种条件进行填充,直到所有符合条件的像素点都被填充为止。floodfill算法常用于图像处理中的填充、边界提取、区域分割等任务。它可以用于实现画图工具中的油漆桶工具,也可以用于图像分割中的区域填充。

floodfill算法的中文名为洪水灌溉算法,基本实现内容:从某个点开始向四周扩散,直到无法再扩散为止。(可以使用DFS和BFS,本文讲解DFS解决)从这个实现内容我们不难发现这很适合我们利用爆搜来实现(一般数据范围不会太大)。如下图数字为1的就是一个性质相同的连通块。这类问题通常会问我们1的最大联通块是多大?或者有几个1的连通块?使用floodfill算法解决的题目基本一眼就能看得出来,不想滑动窗口和动态规划之类的算法,你不试一试是看不出来要使用什么算法的😭😭😭。

算法模板套路: 

创建所需的全局变量(最好设置为静态,因为非静态只有在leetcode上才行)

static boolean[][] vis;//( 不一定要有)

static int[ ] dx = {0 , 0 , 1 , -1 };

static int[ ] dy = {1 , -1 , 0 , 0 };

vis这个布尔类型数组来标记我们已经走过的路,防止重复走导致死循环。如果题目可以改给出的二维数组的值的话可以修改值就不用创建这个数组来标记了。

dx,dy数组配合使用,对应就是上下左右走,这是四个方向(上下左右)的,如果题目要求8个方向的话可以在草稿纸上画个图推一下很快的。

其他的一些条件根据题目的具体情况创建。

DFS模板如下:

其中n为行的个数,m为列的个数,至于要不要回溯要根据题目要求什么来决定。如果是8个方向的话把改一下dx和dy还有k的值变为8即可。x >= 0 && x < n && y >= 0 && y < m 这个可以说是默写了,因为这就是防止越界,每道题目都是这么写的。

public static void dfs(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 < n && y >= 0 && y < m && !vis[x][y] && 题目条件){
                vis[x][y] = true;
                dfs(x,y,.....);
                vis[x][y] = false;//回溯
            }
        }
    }

上面的这些是作者自己总结出来的,友友们刚开始可以试试,后面做的熟练了基本写出这些就是肌肉记忆了,这个在比赛中也会考到。

下面给出的例题前三题必看,可以说是floodfill算法的经典题型了。

例题1:图像渲染

链接:图像渲染

有一幅以 m x n 的二维整数数组表示的图画 image ,其中 image[i][j] 表示该图画的像素值大小。

你也被给予三个整数 sr ,  sc 和 newColor 。你应该从像素 image[sr][sc] 开始对图像进行 上色填充 。

为了完成 上色工作 ,从初始像素开始,记录初始坐标的 上下左右四个方向上 像素值与初始坐标相同的相连像素点,接着再记录这四个方向上符合条件的像素点与他们对应 四个方向上 像素值与初始坐标相同的相连像素点,……,重复该过程。将所有有记录的像素点的颜色值改为 newColor 。

最后返回 经过上色渲染后的图像 

详细题解: 

可以利用深搜,遍历到与该点相连的所有像素相同的点,然后将其修改成指定的像素即可。

这里再强调一下,在leetcode中不加static是可以的,但是在比赛中(要求自己写Main类)只能用static修饰的。 

直接对题目给出的一个点进行DFS,本题是直接在二位数组image的基础上直接修改那么就不用用到 vis 来标记了,因为走过的地方值被修改了那么就不会出现重复走的情况。具体代码如下:

class Solution {
    int[] dx = {0,0,1,-1};
    int[] dy = {1,-1,0,0};
    int prev;
    int m,n;
    public int[][] floodFill(int[][] image, int sr, int sc, int color) {
        if(image[sr][sc] == color){
            return image;
        }
        prev = image[sr][sc];
        m = image.length;
        n = image[0].length;
        dfs(image,sr,sc,color);
        return image;
    }
    public void dfs(int[][] image,int i,int j,int color){
        image[i][j] = color;
        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] == prev){
                dfs(image,x,y,color);
            }
        }
    }
}

例题2:岛屿数量

链接:岛屿数量

给你一个由 '1'(陆地)和 '0'(水)组成的的二维网格,请你计算网格中岛屿的数量。

岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。

此外,你可以假设该网格的四条边均被水包围。

详细题解:

非常经典的一道floodfill算法题目其思路过程为:

遍历整个矩阵,每当找到一块陆地时,岛屿数量 + 1,并且将这个陆地相连的所有陆地,也就是这块岛屿,全部变成海洋。这样的话,我们下次遍历到这块岛屿的时候,它已经是海洋了,不会影响最终结果(当然也可以不修改矩阵,我们可以使用vis数组来完成去重操作)。变成海洋的操作可以利用DFS解决和上面给出的模板差不多😎😎😎

代码实现:

class Solution {
    static int ret;
    static boolean[][] vis;
    static int n;
    static int m;
    static int[] dx = {0,0,1,-1};
    static int[] dy = {1,-1,0,0};
    public static int numIslands(char[][] grid) {
        ret = 0;
        n = grid.length;
        m = grid[0].length;
        vis = new boolean[n][m];
        for(int i = 0;i < n;i++){
            for(int j = 0;j < m;j++){
                if(grid[i][j] == '1' && !vis[i][j]){
                    dfs(grid,i,j);
                    ret++;
                }

            }
        }
        return ret;
    }
    public static void dfs(char[][] grid,int i,int j){
        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 < n && y >= 0 && y < m && grid[x][y] == '1' && !vis[x][y]){
                dfs(grid,x,y);
            }
        }
    }
}

例题3:岛屿的最大面积

链接:岛屿的最大面积

给你一个大小为 m x n 的二进制矩阵 grid 。

岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。

岛屿的面积是岛上值为 1 的单元格的数目。

计算并返回 grid 中最大的岛屿面积。如果没有岛屿,则返回面积为 0 。

没错又是这张图因为比较经典且作者水平有限😭😭😭,所以就直接使用了。

详细题解:

遍历整个矩阵,每当遇到⼀块土地的时候,就用DFS将与这块土地相连的整个岛屿的面积计算出来。然后在搜索得到的所有岛屿面积中求一个最大值即可。这题我们就可以使用vis数组来标记这个位置是否已经被访问过了。

求这种最大值的又是深搜,建议大家自己设置一个变量来专门存储临时值和最大值,我一般存储临时值采用path,存储结果也就是最大值采用ret,step表示递归的层数(这个floodfill算法不涉及,在别的DFS题会用到)这个都行就是自己要明白。

class Solution {
    int[] dx = {0,0,1,-1};
    int[] dy = {1,-1,0,0};
    int ret;
    boolean[][] vis;
    int n,m;
    int path;
    public int maxAreaOfIsland(int[][] grid) {
        n = grid.length;
        m = grid[0].length;
        vis = new boolean[n][m];
        ret = 0;
        for(int i = 0;i < n;i++){
            for(int j = 0;j < m;j++){
                if(grid[i][j] == 1 && !vis[i][j]){
                    path = 0;
                    dfs(grid,i,j);
                    ret = Math.max(ret,path);
                }
            }
        }
        return ret;
    }
    public void dfs(int[][] grid,int i,int j){
        vis[i][j] = true;
        path++;
        for(int k = 0;k < 4;k++){
            int x = i + dx[k];
            int y = j + dy[k];
            if(x >= 0 && x < n && y >= 0 && y < m && 
            grid[x][y] == 1 && !vis[x][y]){
                dfs(grid,x,y);
            }
        }
    }
}

相信做完上面三道题目,友友们是不是觉得有点没有意思,感觉就是在考背诵一样,DFS的写法基本差不多,上面的只要根据题意稍加修改即可。其实DFS确实差不多,有些难点是在如何使用DFS而不是写出DFS,例如下面的例题四,运用到了我们解决问题的经典思路正难则反

例题4:被围绕的区域

链接:被围绕的区域

给你一个 m x n 的矩阵 board ,由若干字符 'X' 和 'O' ,找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O' 用 'X' 填充。

可以看到如果我们不加以转换的话直接按照我们前面三题的思路来的话有点困难,因为要判断很多边界情况,且要进行两遍DFS,所以我们要想一种更为巧妙的解法:正难则反。

解题过程:

可以先利用dfs将与边缘相连的'0' 区域做上标记,然后重新遍历矩阵,将没有标记过的'0' 修改成'X' 即可。就是这么的简单。遍历边缘就画个图模拟一下即可,至于DFS的编写那还不是手到擒来🤩🤩🤩 ,具体代码如下:

class Solution {
    int[] dx = {0,0,1,-1};
    int[] dy = {1,-1,0,0};
    boolean[][] vis;
    int n,m;
    public void solve(char[][] board) {
        n = board.length;
        m = board[0].length;
        vis = new boolean[n][m];
        //标记vis
        for(int i = 0;i < n;i++){
            if(board[i][0] == 'O'){
                dfs(board,i,0);
            }
            if(board[i][m - 1] == 'O'){
                dfs(board,i,m - 1);
            }
        }
        for(int i = 0;i < m;i++){
            if(board[0][i] == 'O'){
                dfs(board,0,i);
            }
            if(board[n - 1][i] == 'O'){
                dfs(board,n - 1,i);
            }
        }
        //将O用X填充
        for(int i = 0;i < n;i++){
            for(int j = 0;j < m;j++){
                if(board[i][j] == 'O' && !vis[i][j]){
                    board[i][j] = 'X';
                }
            }
        }
    }
    public void dfs(char[][] board,int i,int j){
        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 < n && y >= 0 && y < m && board[x][y] == 'O' && 
            !vis[x][y]){
                dfs(board,x,y);
            }
        }
    }
}

总结:这类题难点在如何使用DFS,全局变量要根据具体情况来创建,这些搞定后写个DFS就是默写的事情。

结语:

其实写博客不仅仅是为了教大家,同时这也有利于我巩固知识点,和做一个学习的总结,由于作者水平有限,对文章有任何问题还请指出,非常感谢。如果大家有所收获的话还请不要吝啬你们的点赞收藏和关注,这可以激励我写出更加优秀的文章。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/572433.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【IDEA】在IntelliJ IDEA中导入Eclipse项目:详细指南

IntelliJ IDEA和Eclipse是两款常用的集成开发环境&#xff08;IDE&#xff09;&#xff0c;在软件开发中经常会遇到需要在它们之间迁移项目的情况。本文将重点介绍如何在IntelliJ IDEA中导入Eclipse项目&#xff0c;以帮助开发者顺利地迁移他们的项目&#xff0c;并在IntelliJ …

云主机修复监控插件异常的方法

首先&#xff0c;进入云监控服务--选择主机监控&#xff0c;勾选上网络配置异常的云主机&#xff0c;最上面的修复插件配置&#xff0c;然后等待大约半个小时多&#xff0c;再观察下主机的状态。 一般情况下问题都可以被解决&#xff0c;如果解决不了&#xff0c;可以尝试卸载…

剑指 Offer 03.:数组中重复的数字

剑指 Offer 03. 数组中重复的数字 找出数组中重复的数字。 在一个长度为 n 的数组 nums 里的所有数字都在 0&#xff5e;n-1 的范围内。数组中某些数字是重复的&#xff0c;但不知道有几个数字重复了&#xff0c;也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。…

Linux下的进程管理:创建、终止、切换与等待

文章目录 一、引言二、进程创建1、进程创建的概念与场景2、进程创建的方式a、fork() 系统调用b、fork() 后的执行流程 3、进程创建的过程a、进程创建过程b、子进程创建过程 4、父子进程关系与属性继承 三、进程终止1、进程终止的原因2、进程的错误码和退出码a、错误码b、退出码…

Golang基础5-指针、结构体、方法、接口

指针 和c/c类似&#xff0c;但是go语言中指针不能进行偏移和运算&#xff0c;安全指针 &&#xff08;取地址) *(根据地址取值) nil(空指针&#xff09; make和new之前对比&#xff1a;make用于初始化slice&#xff0c;map&#xff0c;channel这样的引用类型 而new用于类…

热知识:更多团队采用3个及以上内部开发者平台

01 介绍 根据 Perforce Puppet 的一份新报告中&#xff0c;平台工程的采用已经在一些企业内看到了成效&#xff0c;78% 的受访者表示他们的组织拥有专门的平台团队至少三年了。 然而&#xff0c;这并不意味着这些组织只使用同一套工具。四分之三的调查参与者表示&#xff0c;他…

如何使用SOLIDWORKS添加装饰螺纹线规格

在我们的设计过程中&#xff0c;有很多的时候螺纹规格在机械设计手册上没有&#xff0c;而我们的SOLIDWORKS软件里面录制的都是符合标准的的螺纹&#xff0c;至于其他的特种或者超出的规格需要我们设计人员去手工添加&#xff0c;以下介绍我们装饰螺纹线新规格的添加方法&#…

关于PMO卓越中心职能建设的实践与思考︱PMO大会

全国PMO专业人士年度盛会 浪潮电子信息产业股份有限公司PMO时军先生受邀为PMO评论主办的2024第十三届中国PMO大会演讲嘉宾&#xff0c;演讲议题为“让组织持续卓越——关于PMO卓越中心职能建设的实践与思考”。大会将于5月25-26日在北京举办&#xff0c;敬请关注&#xff01; …

菜单访问url/接口url为什么要带时间戳

一&#xff0c; 问题 1&#xff0c;菜单url中如果不加时间戳&#xff0c;会导致什么问题。我们现在做一个东西&#xff0c;需要获取菜单的访问地址&#xff0c;我们要拼这个地址 2&#xff0c;查询接口中&#xff0c;时间戳&#xff0c;如果不加&#xff0c;具体导致什么问题 二…

Vue集成three.js,加载glb、gltf类型的3d模型

安装基本依赖 // 注意OrbitControls要加{}&#xff0c;注意路径是jsm import { OrbitControls } from ‘three/examples/jsm/controls/OrbitControls.js’; // import { dat } from ‘three/examples/jsm/controls/dat.gui.js’; // dat gui这个插件&#xff0c;是另外自己下载…

杰理使用USB声卡模式时关闭MIC

杰理在使用PC模式的时候&#xff0c;想只保留扬声器&#xff0c;但不要打开MIC功能&#xff0c;可以配置USB_DEVICE_CLASS_CONFIG中把MIC_CLASS去掉&#xff0c;然后重新编译就可以了。

Kimi 高效使用技巧,80%的人都不知道

关注我, AI 学习之旅上&#xff0c;我与您一同成长&#xff01; 一、引言 Kimi 作为国产之光&#xff0c;在过去的一个多月里成为国内大模型的香饽饽。据数据分析&#xff0c;Kimi 网页、APP、小程序等各端的日活已经突破 300 万&#xff0c;超过文心一言、通义千问、智谱清言…

单链表实现通讯录

不过多赘述了 顺序表的增删查改-CSDN博客https://blog.csdn.net/bkmoo/article/details/137566495?spm1001.2014.3001.5502 使用顺序表实现通讯录-CSDN博客https://blog.csdn.net/bkmoo/article/details/137676561?spm1001.2014.3001.5502这里没有使用文件操作只是简单的使…

6.MMD ray渲染 材质的添加及打光方法

材质 前置准备 先准备好模型和场景 将ray控制器拖入进去 添加完默认的材质以后的效果 打开插入材质页面 打开MaterialMap栏 将流萤的模型展开 自发光 现在给领带添加一个自发光效果 在自发光Emissive里&#xff0c;打开x1&#xff0c;选择albedo&#xff0c;白光 现在…

使用微软Phi-3-mini模型快速创建生成式AI应用

微软Phi-3大语言模型是微软研究院推出的新一代系列先进的小语言模型。Phi-3系列包括phi-3-mini、phi-3-small和phi-3-medium三个不同规模的版本。这些模型在保持较小的参数规模的同时&#xff0c;通过精心设计的训练数据集和优化的算法&#xff0c;实现了与大型模型相媲美的语言…

【CVPR2023】Re:InterHand:一个用于3D交互手部姿态估计的重光照数据集

这篇论文的标题是《A Dataset of Relighted 3D Interacting Hands》&#xff0c;作者是Gyeongsik Moon, Shunsuke Saito, Weipeng Xu, Rohan Joshi, Julia Buffalini, Harley Bellan, Nicholas Rosen, Jesse Richardson, Mallorie Mize, Philippe de Bree, Tomas Simon, Bo Pen…

玩转PyCharm

玩转PyCharm PyCharm是由JetBrains公司开发的提供给Python专业的开发者的一个集成开发环境&#xff0c;它最大的优点是能够大大提升Python开发者的工作效率&#xff0c;为开发者集成了很多用起来非常顺手的功能&#xff0c;包括代码调试、高亮语法、代码跳转、智能提示、自动补…

MyBatis 核心配置讲解(上)

大家好&#xff0c;我是王有志&#xff0c;一个分享硬核 Java 技术的互金摸鱼侠。 前两篇的文章中我们分别介绍了 MyBatis 和 MyBaits 的应用组成&#xff0c;到这里基础篇的内容就结束了。 从今天开始&#xff0c;我们正式进入 MyBatis 学习的第二阶段&#xff1a;MyBatis 的…

【QT学习】9.绘图,三种贴图,贴图的转换,不规则贴图(透明泡泡),简单绘图工具制作

一。绘图的解释 Qt 中提供了强大的 2D 绘图系统&#xff0c;可以使用相同的 API 在屏幕和绘图设备上进行绘制&#xff0c;它主要基于QPainter、QPaintDevice 和 QPaintEngine 这三个类。 QPainter 用于执行绘图操作&#xff0c;其提供的 API 在 GUI 或 QImage、QOpenGLPaintDev…

maya blendshape

目录 shape编辑器 maya创建blendshape python 脚本 添加形变动画 查看顶点个数 shape编辑器 打开方式&#xff1a; 窗口-动画编辑器-形变编辑器 maya创建blendshape python 脚本 import maya.cmds as cmds# 创建基础网格 - 球体 baseMesh cmds.polySphere(name"bas…