【算法/学习】:flood算法

news2024/11/17 12:37:48

✨                                                 君子坐而论道,少年起而行之        🌏 

📃个人主页:island1314

🔥个人专栏:算法学习

🚀 欢迎关注:👍点赞 👂🏽留言 😍收藏  💞 💞 💞


引言

FloodFill 泛洪填充)算法是一种图像处理的基本算法,用于填充连通区域。该算法通常从一个种子点开始,沿着种子点的相邻像素进行填充,直到遇到边界或者其他指定的条件为止。FloodFill 算法的主要应用是在图像编辑软件中实现填充操作,以及在计算机图形学、计算机视觉等领域中进行区域填充。

  • 基本作用:寻找连通块
  • 基本方法:DFS(深度优先遍历)、BFS(广度优先遍历)
  • 适用题目:需要找出分类块的题目/一些聚类问题/ 
  • 本质问题:找出这块区域中性质相同的连通块

    连通块可以使上下左右四个方向连同,也可以是加上斜向八个方向连通

算法的基本思想是:

  • 选择一个起始点(种子点),将其染色。
  • 检查当前点的相邻像素,如果符合填充条件且未被染色,则将其染色,并将其加入待处理队列(或递归调用)。
  • 重复步骤2,直到没有可填充的像素为止。

Flood Fill 算法的实现可以使用递归、栈或队列等数据结构。常见的填充条件包括相邻像素颜色相同、相邻像素颜色不同等。

在图像编辑软件中,用户通常通过选择一个起始点和指定填充颜色来触发 Flood Fill 操作,使得相邻区域被填充为指定颜色。

Flood Fill 算法的变种和优化版本也被用于解决其他问题,例如计算连通区域的大小、边界填充、种子点选择策略等。

图解如下:找到负数相连连通块数目‘

下面我们通过一些题目来理解这个算法思想:


1. 图像渲染

思路:

        我们从给定的起点开始,可以利用「深搜」或者「宽搜」(上下左右四个方向)。每次搜索到一个方格时,如果其与初始位置的方格颜色相同,就将该方格的颜色更新,如果不相同,则进行回溯。这里我们设置初始方格为target.

优化:

可以使用一个bool类型的数组来标记这个点是否被染色,以防止重复染色。

AC代码如下:

int dir[4][2] = {
    {1,0},{0,1},{-1,0},{0,-1}
};
int n, m, target;
bool vis[105][105]; //标记该点是否用过

void dfs(vector<vector<int>>& image, int x, int y, int color){
    image[x][y] = color;
    vis[x][y] = true;
    for (int i = 0; i < 4; i++) {
        int dx = x + dir[i][0], dy = y + dir[i][1];
        if (dx >= 0 && dx < m && dy >= 0 && dy < n && !vis[dx][dy] && image[dx][dy] == target)
            dfs(image, dx, dy, color);
    }
}

vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {
    if (image[sr][sc] == color) return image;
    m = image.size(), n = image[0].size();
    target = image[sr][sc];
    dfs(image, sr, sc, color);
    return image;
}

2. 岛屿数量

思路:

  • 遍历整个矩阵,每次找到「⼀块陆地」的时候:
  •  说明找到「⼀个岛屿」,记录到最终结果 res⾥⾯;
  • 并且将这个陆地相连的所有陆地,也就是这块「岛屿」,全部「变成海洋」。这样的话,我们下次 遍历到这块岛屿的时候,它「已经是海洋」了,不会影响最终结果。
  •  其中「变成海洋」的操作,可以利⽤「深搜」来解决

AC代码如下:

int dir[4][2] = {
    {1,0},{0,1},{-1,0},{0,-1}
};
int n, m;
bool vis[505][505]; //标记该点是否用过

void dfs(vector<vector<char>>& grid, int x, int y){
    vis[x][y] = true;
    for (int i = 0; i < 4; i++) {
        int dx = x + dir[i][0], dy = y + dir[i][1];
        if (dx >= 0 && dx < m && dy >= 0 && dy < n && !vis[dx][dy] && grid[dx][dy] == '1')
        {
            dfs(grid, dx, dy);
        }
    }
}

int numIslands(vector<vector<char>>& grid) {
    m = grid.size(), n = grid[0].size();
    int ret = 0;
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++){
            if (!vis[i][j] && grid[i][j] == '1') {
                ret++;
                dfs(grid, i, j);
            }
        }
    }
    return ret;
}

3. 岛屿的最大面积

思路:

在解决这个问题时,我们可以遍历整个矩阵,每当遇到一块土地时,使用深度搜索(DFS)或宽度搜索(BFS)将与这块土地相连的整个岛屿的面积计算出来。然后,在搜索得到的所有岛屿面积中求一个最大值即可。

在搜索过程中,为了防止搜到重复的土地,可以采用以下两种方法之一:

  1. 开一个同等规模的布尔数组,标记这个位置是否已经被访问过;
  2. 将原始矩阵的 1 修改成 0,但是这样操作会修改原始矩阵。

这样的搜索过程能够找到所有相连的土地形成的岛屿,并求出它们的面积,最后取得所有岛屿的最大面积。

AC代码如下:

int dir[4][2] = {
    {1,0},{0,1},{-1,0},{0,-1}
};
int n, m, cnt;
bool vis[505][505]; //标记该点是否用过

void dfs(vector<vector<int>>& grid, int x, int y){
    vis[x][y] = true;
    cnt++; //记录每块岛屿的面积
    for (int i = 0; i < 4; i++) {
        int dx = x + dir[i][0], dy = y + dir[i][1];
        if (dx >= 0 && dx < m && dy >= 0 && dy < n && !vis[dx][dy] && grid[dx][dy] == 1)
        {
            dfs(grid, dx, dy);
        }
    }
}

int maxAreaOfIsland(vector<vector<int>>& grid) {
    m = grid.size(), n = grid[0].size();
    int ret = 0; //统计最大数量
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (!vis[i][j] && grid[i][j] == 1) {
                cnt = 0;
                dfs(grid, i, j);
                ret = max(cnt, ret);
            }
        }
    }
    return ret;
}

4. 被围绕的区域

思路:

 正难则反。 这里我们采用逆向思维的方式,先从四周开始深度遍历,将能遍历到的 ‘O’ 做上标记,那么剩下的字符 ‘O’ 就是需要修改的字符了,之后再重新遍历棋盘,将没有标记的 ‘O’ 改为 ‘X’  。

 AC代码如下:

int dir[4][2] = {
    {1,0},{0,1},{-1,0},{0,-1}
};
int n, m, cnt;
bool vis[505][505]; //标记该点是否用过

void dfs(vector<vector<char>>& grid, int i, int j){
    vis[i][j] = true;
    for (int k = 0; k < 4; k++) {
        int x = i + dir[k][0], y = j + dir[k][1];
        if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y] == 'O')
        {
            dfs(grid, x, y);
        }
    }
}

void solve(vector<vector<char>>& board) {
    m = board.size(), n = board[0].size();
    for (int i = 0; i < n; i++) { //最上面和最下面两行
        if (board[0][i] == 'O') dfs(board, 0, i);
        if (board[m - 1][i] == 'O') dfs(board, m - 1, i);
    }
    for (int i = 1; i < m - 1; i++) { //最左边和最右边两列
        if (board[i][0] == 'O') dfs(board, i, 0); 
        if (board[i][n - 1] == 'O') dfs(board, i, n - 1);
    }

    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (board[i][j] == 'O' && !vis[i][j])
                board[i][j] = 'X';
        }
    }
    
}

5. 太平洋大西洋水流问题

思路:

题目本质:雨水只能从高的地方流向低的地方,求二维表格中哪些坐标的雨水既可以流入太平洋又可以流入大西洋。

和上面一样,这里我们也采用正难则反的思想。因为如果直接去判断某⼀个位置是否既能到⼤西洋也能到太平洋,会重复遍历很多路径。因此我们反着来,去从太平洋和大西洋两边的开始反向遍历,比自己本身大就继续向上遍历,最后两边都遍历完求重叠区域(即被标记两次的点),就是为我们要求的答案。

关于标记两次,我们用两个bool数组即可。

AC代码如下:

int dir[4][2] = {
    {1,0},{0,1},{-1,0},{0,-1}
};
int n, m, cnt;
bool pac[505][505]; 
bool atl[505][505];

void dfs(vector<vector<int>>& heights, int i, int j, bool vis[][505]) {
    vis[i][j] = true;
    for (int k = 0; k < 4; k++) {
        int x = i + dir[k][0], y = j + dir[k][1];
        if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && heights[x][y] >= heights[i][j])
        {
            dfs(heights, x, y, vis);
        }
    }
}


vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights) {
    m = heights.size(), n = heights[0].size();
    vector<vector<int>> ret;

    // 1.太平洋的流入
    for (int j = 0; j < n; j++) dfs(heights, 0, j, pac); //第一行
    for (int i = 0; i < m; i++) dfs(heights, i, 0, pac); //第一列

    // 2.大西洋的流入
    for (int j = 0; j < n; j++) dfs(heights, m - 1, j, atl); //最后一行
    for (int i = 0; i < m; i++) dfs(heights, i, n - 1, atl); //最后一列

    // 3.求重叠区域
    for (int i = 0; i < m; i++){
        for (int j = 0; j < n; j++){
            if (pac[i][j] && atl[i][j])ret.push_back({ i,j });
        }
    }

    return ret;
}

6. 扫雷游戏

AC代码如下:

思路:

模拟类型的深度优先搜索(DFS)题目。首先,需要理解题目的要求,即游戏规则。

从给定的点击位置开始,根据游戏规则执行一次深度优先搜索即可。在DFS的过程中,如果当前遍历的格子没有地雷,就往下遍历,如果有,就显示附近地雷的数量,然后递归返回。

注意:八个方向都可以扩展

int dir[8][2] = {
    {1,0},{0,1},{-1,0},{0,-1},
    {-1,-1},{-1,1},{1,-1},{1,1}
};
int n, m;

void dfs(vector<vector<char>>& board, int i, int j)
{
    // 1. 统计以下周围地雷个数
    int cnt = 0;
    for (int k = 0; k < 8; k++)
    {
        int x = i + dir[k][0], y = j + dir[k][1];
        if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'M')
        {
            cnt++;
        }
    }
    // 2.周围有地雷
    if (cnt)
    {
        board[i][j] = cnt + '0'; //显示附近地雷数量
        return;
    }
    else // 3.周围没地雷,则往八个方向进行扩展
    {
        board[i][j] = 'B';//周围没地雷时,自己标记为B
        for (int k = 0; k < 8; k++) {
            int x = i + dir[k][0], y = j + dir[k][1];
            if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'E')
            {
                dfs(board, x, y);
            }
        }
    }
}
vector<vector<char>> updateBoard(vector<vector<char>>& board, vector<int>& click) 
{
    m = board.size(), n = board[0].size();
    int sx = click[0], sy = click[1]; //起始位置
    if (board[sx][sy] == 'M') 
    {
        board[sx][sy] = 'X'; //扫到地雷直接退出
        return board;
    }
    dfs(board, sx, sy);

    return board;
}

7. 衣橱整理

思路:

在原始的dfs基础上加上一个边界条件,即不可超过的范围即可。

AC代码如下:

int dir[4][2] = {
    {1,0},{0,1},{-1,0},{0,-1},
};
int n, m, cnt, ret = 0;
bool vis[505][505];

int digit(int x) //x的数位之和额外的条件,
{
    int ans = 0;
    while (x)
    {
        ans += x % 10;
        x /= 10;
    }
    return ans;
}

void dfs(int i, int j){
    vis[i][j] = true;
    ret++;
    for (int k = 0; k < 4; k++) {
        int x = i + dir[k][0], y = j + dir[k][1];
        if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && (digit(x) + digit(y) <= cnt))//不整理digit(x) + digit(y) > cnt;
        {
            dfs(x, y);
        }
    }
}

int wardrobeFinishing(int _m, int _n, int _cnt) {
    m = _m, n = _n, cnt = _cnt; //改成全局变量存储
    dfs(0, 0); //从(0,0)开始整理
    return ret;
}

8. 城市群数量

 思路:

正常的连通问题,dfs加标记即可

 AC代码如下:(dfs解法)

int n;
bool vis[210] = { 0 }; //标记当前位置释放被搜过
void dfs(vector<vector<int>>& m, int pos){
	vis[pos] = true; //连通城市
	for (int i = 0; i < n; i++) {
		if (!vis[i] && m[pos][i] == 1) {
			dfs(m, i);
		}
	}
}
int citys(vector<vector<int> >& m) {
	n = m.size();
	int ret = 0;
	for (int i = 0; i < n; i++) {
		if (!vis[i]) {
			ret++; //记录城市群数量
			dfs(m, i);
		}
	}
	return ret;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

鸿蒙交互事件开发01——点击/拖拽/触摸事件

如果你也对鸿蒙开发感兴趣&#xff0c;加入“Harmony自习室”吧&#xff01;扫描下方名片&#xff0c;关注公众号&#xff0c;公众号更新更快&#xff0c;同时也有更多学习资料和技术讨论群。 1 概 述 事件是人机交互的基础&#xff0c;鸿蒙开发中&#xff0c;事件分…

EmguCV学习笔记 VB.Net 2.1 颜色空间和颜色

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 EmguCV学习笔记目录 Vb.net EmguCV学习笔记目录 C# 笔者的博客网址&#xff1a;VB.Net-CSDN博客 教程相关说明以及如何获得pdf教程…

威胁组织伪造Loom,Mac用户警惕AMOS窃取软件威胁

近期&#xff0c;一个复杂且可能与神秘威胁组织“Crazy Evil”有关联的网络犯罪活动&#xff0c;已将注意力转向了Mac用户群体。该组织利用广受欢迎的屏幕录制工具Loom作为掩护&#xff0c;悄无声息地传播着臭名远扬的AMOS数据窃取软件。Moonlock Lab的安全研究员们揭开了这一阴…

【数据结构与算法 | 图篇】拓扑排序

1. 概念 拓扑排序是是一种针对有向无环图进行的排序方法。它将图中所有顶点排成一个线性序列&#xff0c;使得对于图中的每一条有向边(u, v)&#xff0c;顶点u在序列中都出现在顶点v之前。 适用范围&#xff1a; 拓扑排序只适用于有向无环图。 结果非唯一&#xff1a; 对于…

阿里云ubuntu系统安装mysql8.0

一、安装mysql8.0 1.已安装其他版本的mysql&#xff0c;需要删除 若没有不需要此操作 1 #卸载MySQL5.7版本 2 apt remove -y mysql-client5.7* mysql-community-server5.7* 4 # 卸载5.7的仓库信息 5 dpkg-l | grep mysql | awk iprint $2} | xargs dpkg -P2.更新仓库 apt u…

FASTSPEECH 2论文阅读

FASTSPEECH 2: FAST AND HIGH-QUALITY END-TOEND TEXT TO SPEECH 现状 非自回归模型可以在质量相当的情况下显著快于先前的自回归模型合成模型。但FastSpeech模型训练依赖与自回归教师模型进行时长预测&#xff08;提供更多的信息作为输入&#xff09;和知识蒸馏&#xff08;…

【开端】Java中判断一个对象是否是空内容

一、绪论 在Java中&#xff0c;我们常常使用的到的就是封装&#xff0c;为什么要封装&#xff0c;封装有什么好处。首先在系统开发过程中&#xff0c;其实很多功能和场景都共性的。那么为了避免重复造轮子&#xff0c;我们这时就使用到了封装。封装可以一次造轮子&#xff0c;无…

数据集搜索

1. 数据集和数据集的分类 数据集是一组数据的集合&#xff0c;通常用于机器学习、统计分析、数据挖掘等领域&#xff0c;帮助算法训练、模型验证和评估。可以是各种形式的数据&#xff0c;如表格、图像、机器学习相关的文件等。 根据在机器学习中的应用&#xff0c;数据集可以…

1. MongoDB概念解析

1. 概念解析 在 MongoDB 中基本的概念是文档、集合、数据库。 SQL 术语/概念MongoDB 术语/概念解释/说明databasedatabase数据库tablecollection数据库表/集合rowdocument数据记录行/文档columnfield数据字段/域indexindex索引table joins表连接,MongoDB不支持primary keypri…

1.3 数据库的发展历史与演变

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;欢迎订阅相关专栏&#xff1a; 工&#x1f497;重&#x1f497;hao&#x1f497;&#xff1a;野老杂谈 ⭐️ 全网最全IT互联网公司面试宝典&#xff1a;收集整理全网各大IT互联网公司技术、项目、HR面试真题.…

鸿萌数据恢复服务: 如何修复 SQL Server 数据库错误 829?

天津鸿萌科贸发展有限公司从事数据安全服务二十余年&#xff0c;致力于为各领域客户提供专业的数据恢复、数据备份、网络及终端数据安全等解决方案与服务。 同时&#xff0c;鸿萌是众多国际主流数据恢复软件(Stellar、UFS、R-Studio、ReclaiMe Pro 等)的授权代理商&#xff0c…

pandas 笔记crosstab

用来计算两个&#xff08;或更多&#xff09;因子的交叉表&#xff08;即频率表、列联表或透视表&#xff09;。这个功能特别适用于统计分析和数据探索阶段&#xff0c;帮助理解不同变量之间的关系 1 基本用法 pd.crosstab(index, columns, valuesNone, rownamesNone, colnam…

基于UE5和ROS2的激光雷达+深度RGBD相机小车的仿真指南(二)---ROS2与UE5进行图像数据传输

前言 本系列教程旨在使用UE5配置一个具备激光雷达深度摄像机的仿真小车&#xff0c;并使用通过跨平台的方式进行ROS2和UE5仿真的通讯&#xff0c;达到小车自主导航的目的。本教程默认有ROS2导航及其gazebo仿真相关方面基础&#xff0c;Nav2相关的学习教程可以参考本人的其他博…

HarmonyOS-MPChart以X轴或y轴为区间设置不同颜色

本文是基于鸿蒙三方库mpchart OpenHarmony-SIG/ohos-MPChart 的使用&#xff0c;以X轴为区间设置不同的曲线颜色。 mpchart本身的绘制功能是不支持不同区间颜色不同的曲线的&#xff0c;那么当我们的需求曲线根据x轴的刻度区间绘制不同颜色&#xff0c;就需要自定义绘制方法了。…

LVS (Linux virual server)

LVS简介 LVS&#xff08;Linux Virtual Server&#xff09;是一个基于Linux平台的开源负载均衡系统。它通过将多个服务器组成一个虚拟服务器集群&#xff0c;实现了高效的负载均衡和流量分发。 LVS的核心思想是利用IP负载均衡技术和内容请求分发机制&a…

传知代码-【CLIP】文本也能和图像配对

代码以及视频讲解 本文所涉及所有资源均在传知代码平台可获取 概述 模态&#xff1a;数据的一种形式&#xff0c;如图像、文本、声音、点云等。 多模态学习&#xff0c;就是利用模型同时处理多个模态数据&#xff0c;有助于提高模型的准确性和泛化能力。在自动驾驶场景中&am…

利用住宅代理应对机器人流量挑战:识别、使用与检验指南

引言 什么是机器人流量&#xff1f;其工作原理是什么&#xff1f; 机器人流量来自哪里&#xff1f; 合法使用机器人时如何避免被拦截&#xff1f; 如何检验恶意机器人流量&#xff1f; 总结 引言 你是否曾经遇到过访问某个网站时&#xff0c;被要求输入验证码或完成一些其…

源代码加密的意义和办法?

一、源代码加密的意义1、防止恶意修改&#xff1a;未加密的源代码容易被恶意用户或竞争对手获取并修改&#xff0c;以植入恶意代码或病毒&#xff0c;损害软件的功能性和安全性。加密后的源代码即使被非法获取&#xff0c;也无法修改或理解&#xff0c;从而防止了被破坏的风险。…

品味白酒的四大步骤,体验不一样的美酒人生

在华夏千年的文化传承中&#xff0c;白酒如同一部厚重的历史长卷&#xff0c;每一滴都蕴含着丰富的故事与智慧。豪迈白酒&#xff08;HOMANLISM&#xff09;&#xff0c;作为这长卷中的璀璨篇章&#xff0c;更是以其不同的魅力&#xff0c;吸引着无数品鉴者去探寻其中的奥秘。今…

android13 禁用wifi

总纲 android13 rom 开发总纲说明 目录 1.前言 2.情况分析 3.代码分析 3.1 代码位置1 3.2 代码位置2 3.3 代码位置3 4.代码修改 5. 彩蛋 1.前言 这个文章讲的是,在frameworks里面禁止打开wifi。 2.情况分析 我们打开wifi一般是 public static void turnOnWifi(Co…