【算法】——学会floodfill算法,周围都是舒适区

【算法】——学会floodfill算法,周围都是舒适区

🌟 ​​前言:Flood Fill——像素世界的「魔法棒」​

想一键填满图中的封闭区域?
游戏角色为何不会穿墙?
这背后都藏着​​Flood Fill算法​​的魔法!

从画图软件的「油漆桶」到迷宫路径计算,
这个用​​DFS/BFS​​模拟「洪水蔓延」的算法,
能用10行代码解决许多看似复杂的问题。

本文将带你:
🔹 掌握递归经典实现
🔹 破解「4连通」与「8连通」的秘密
🔹 搞定一些经典问题

​让我们开始这场「数字洪水」的冒险吧!​​ 🎨🌊

"好的算法就像魔法——Flood Fill就是那根点像素成金的魔杖"

🌟 ​​Flood Fill算法:数字世界的「颜料蔓延术」​

​🔍 算法本质​

一种用于​​填充连通区域​​的经典算法,通过指定起点和填充规则,像倒颜料般自动填满封闭区域。

​⚡ 核心思想​

  1. ​选定起点​​:从像素/网格的某个点出发
  2. ​扩散规则​​:
    • ​4连通​​:上下左右4个方向
    • ​8连通​​:增加斜向共8个方向(更易漏边)
  3. ​停止条件​​:遇到边界色或已填充区域

​💡 两种经典实现​

🔍 基础版(DFS递归实现)​

#include <vector> #include <iostream> using namespace std; // 4方向移动:上右下左 const int dx[] = {-1, 0, 1, 0}; const int dy[] = {0, 1, 0, -1}; void dfs(vector<vector<int>>& image, int x, int y, int oldColor, int newColor) { // 边界检查 + 颜色检查 if(x < 0 || x >= image.size() || y < 0 || y >= image[0].size() || image[x][y] != oldColor || image[x][y] == newColor) return; image[x][y] = newColor; // 填充新颜色 // 递归4个方向 for(int i = 0; i < 4; ++i) { dfs(image, x + dx[i], y + dy[i], oldColor, newColor); } } vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int newColor) { int oldColor = image[sr][sc]; if(oldColor != newColor) dfs(image, sr, sc, oldColor, newColor); return image; }

​⚡ 优化版(BFS队列实现)​

#include <queue> #include <utility> // for pair vector<vector<int>> floodFill_BFS(vector<vector<int>>& image, int sr, int sc, int newColor) { int oldColor = image[sr][sc]; if(oldColor == newColor) return image; queue<pair<int, int>> q; q.push({sr, sc}); image[sr][sc] = newColor; while(!q.empty()) { auto [x, y] = q.front(); q.pop(); for(int i = 0; i < 4; ++i) { int nx = x + dx[i], ny = y + dy[i]; if(nx >= 0 && nx < image.size() && ny >= 0 && ny < image[0].size() && image[nx][ny] == oldColor) { image[nx][ny] = newColor; q.push({nx, ny}); } } } return image; }

​🚀 实际应用场景​

  • 画图软件的油漆桶工具
  • 游戏中的地图可达性判断
  • 图像处理(背景替换/抠图)
  • LeetCode「岛屿数量」「图像渲染」等题型

​🌈 趣味比喻​

把算法想象成​​病毒传播​​:

  • ​DFS​​:一个感染者拼命传染能接触的所有人
  • ​BFS​​:感染者们按批次有序传播
"从一滴颜料到染满整个池塘——Flood Fill用代码诠释了什么是真正的『蔓延美学』"

🌟 ​​使用Flood Fill算法解决经典问题

经典例题:岛屿问题

思路:

直接遍历这个二维网格,遇到‘1’,就对这个网格相邻的最多四个网格以及它们相邻的区域进行判断

因为一个网格重复判断是没有意义的,并且相邻的为‘1’的网格视为一个网格

我们需要一个等大的bool类型的二维数组来一一对应二维网格中的某个网格是否已经被检查过如果被检查过,则不用检查如果没有被检查过,则需要对其以及其周边区域进行检查

这就是记忆化搜索,bool类型的二维数组就相当于一个记忆存储器

此外,因为需要对周边相邻的四个方向检查,我们可以提前创建两个数组来模拟四个方向

代码实现:

class Solution { public: int m; int n; int ret; vector<vector<bool>> vis; int numIslands(vector<vector<char>>& grid) { m = grid.size(); n= grid[0].size(); vis=vector<vector<bool>>(m,vector<bool>(n,false)); for(int i=0;i<m;i++){ for(int j=0;j<n;j++){ if(grid[i][j] == '1'){ if(!vis[i][j]) { ret++; dfs(grid,i,j); } } } } return ret; } int dx[4]={0,0,1,-1}; int dy[4]={-1,1,0,0}; void dfs(vector<vector<char>>& grid,int i,int j){ vis[i][j] = true; for(int k=0;k<4;k++){ int x = dx[k]+i; int y = dy[k]+j; if(x>=0 && x<m && y>=0 && y<n && !vis[x][y] && grid[x][y]=='1') dfs(grid,x,y); } } };

经典例题:图像渲染

 思路:

这种问题一般大差不差,有了上面一题的基础,这道思路也很清晰

直接对目标节点进行判断,如果需要上色就上色,并且对其周围区域以及周围区域的周围区域判断即可

代码实现:

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

经典例题:太平洋大西洋水流问题

如果是以前,大家会连这道题目要干什么都看不到,不信看看下面这位暴躁老哥的发言

 

思路:

题目要求我们输出既可以流向大西洋又可以流向太平洋的所有格子一个格子要流向太平洋,就需要有一条可以递减格子路径到太平洋一个格子要流向大西洋,就需要一条可以递减格子路径到大西洋 

如例子中的[2,2]格子它的右方向按照 5->3-1递减,流向大西洋它的左方向按照 5->4->2递减,流向太平洋

所以,我们需要对一个格子进行深搜,在其周围找到一条通往大西洋 一条通往太平洋的路径

这是解题思路,但你落实下去,就会发现难得可怕

这时候,正面突破就行不通,我们就需要从其他方面思考一下与其求一个格子,同时到太平洋、大西洋不如先判断一个格子上的水,是否能到太平洋,再判断是否能到大西洋

两个的结果都是对的,则符合题目要求,输出即可

解题思路就变成:先找到所有水流能流向太平洋的格子再找到所有水流能流向大吸引的格子在遍历一次所有格子,如果一个格子满足上述两个条件,就是我们需要的格子!

代码实现:

class Solution { public: int m = 0; int n = 0; int dx[4]={-1,1,0,0}; int dy[4]={0,0,-1,1}; void dfs(int i,int j,vector<vector<bool>>& vis,vector<vector<int>>& heights){ vis[i][j]=true; for(int k=0;k<4;k++){ int x = i+dx[k]; int y = j+dy[k]; if(x<m && x>=0 && y<n && y>=0 && !vis[x][y] && heights[i][j] <= heights[x][y] ) { dfs(x,y,vis,heights); } } } vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights) { m=heights.size(); n=heights[0].size(); vector<vector<int>> re_value; vector<vector<bool>> atl(m,vector<bool>(n,false)); vector<vector<bool>> pac(m,vector<bool>(n,false)); for(int j=0;j<n;j++){ dfs(0,j,pac,heights); dfs(m-1,j,atl,heights); } for(int i=0;i<m;i++){ dfs(i,0,pac,heights); dfs(i,n-1,atl,heights); } for(int i=0;i<m;i++){ for(int j=0;j<n;j++){ if(pac[i][j] && atl[i][j]){ re_value.push_back({i,j}); } } } return re_value; } };

🌟 ​​结语:Flood Fill的边界与想象​

Flood Fill算法就像数字世界的水彩笔,用最简单的规则解决了区域填充的核心问题。从图像编辑软件的魔术棒到游戏地图的可达性判断,这个经典算法的应用远比我们看到的更广泛。

本文介绍的实现方式只是冰山一角。在实际开发中,针对不同场景可能需要考虑内存优化、并行计算等进阶技巧。但无论形式如何变化,其核心思想始终如一:​​通过系统性的蔓延规则,完成区域的智能填充​​。

当你下次使用绘图软件时,不妨想想背后这个精妙的算法。或许这就是编程的魅力——用简洁的代码逻辑,实现看似复杂的视觉魔法。

"好的算法如同魔法,Flood Fill正是那支点石成金的魔杖。"

​继续探索吧,更多算法奇迹等着你去发现!​​ 🚀

Read more

数据库从零开始:MySQL 中的 DDL 库操作详解【Linux版】

数据库从零开始:MySQL 中的 DDL 库操作详解【Linux版】

前言         在上一篇文章中,我们深入探讨了 MySQL 的基础知识,为大家奠定了坚实的理论基础。今天,我们将目光聚焦于 MySQL 最基础且至关重要的操作之一——数据库库级别的数据定义语言(DDL)操作,这是每一个数据库开发者和管理者必须精通的技能。         库级 DDL 操作是构建和管理数据库系统的基础,它涉及数据库对象的创建、修改和删除。通过本文,我们将详细讲解如何有效地进行数据库的管理,包括: 1. 创建数据库的基本语法和注意事项 2. 选择和切换数据库的正确方法 3. 修改数据库字符集和校对规则 4. 安全有效地删除数据库         接下来,让我们一步步揭开 MySQL 库操作的神秘面纱,帮助读者全面掌握这些核心技能。 1.创建数据库         我们先从数据库的创建开始讲起,相信看过我上篇文章的读者朋友见识过我常见数据库,上篇仅仅是为了让各位快速了解数据库,今天才是正事对它的讲解,下面我先带领各位看看它的语法。 1.1.语法 CREATE DATABASE [IF NOT EXISTS]

By Ne0inhk
RabbitMQ

RabbitMQ

在消息队列(MQ)中,确保消息成功传递是一个关键问题。消息传递的过程通常包括以下几个阶段:publisher(生产者) -> exchange(交换机) -> queue(队列) -> consumer(消费者)。为了确保消息在每个阶段都能成功传递,我们需要采取一系列措施来保证消息的可靠性。 生产者的可靠性 重试机制 当生产者与交换机(或队列,如果没有交换机)之间的连接不稳定时,生产者发送的消息可能会在传输过程中丢失。在这种情况下,生产者需要等待一段时间以获取响应。如果未收到响应,生产者应尝试重新发送消息。重试次数应有限制,以防止因持续重试而占用过多资源。此外,重试之间应有一定的间隔时间,以避免频繁重试导致资源浪费。 由于发送消息时会占用通道,其他业务操作可能会被阻塞,直到消息发送完成(无论成功或失败)。因此,对发送消息的重试机制进行限制是必要的,以防止因连接问题导致资源被长时间占用。 以下是一个在Spring Boot中配置生产者重试机制的示例: spring: rabbitmq: connection-timeout: 1s

By Ne0inhk

使用 VS Code 连接 MySQL 数据库

文章目录 * 前言 * VS Code下载安装 * 如何在VS Code上连接MySQL数据库 * 1、打开扩展 * 2、安装MySQL插件 * 3、连接 * 导入和导出表结构和数据 前言 提示:这里可以添加本文要记录的大概内容: 听说VS Code不要钱,功能还和 Navicat 差不多,还能在上面打游戏 但是没安装插件是不行的 发现一个非常牛的博主 还有一个非常牛的大佬 提示:以下是本篇文章正文内容,下面案例可供参考 VS Code下载安装 VS Code下载安装 如何在VS Code上连接MySQL数据库 本篇分享是在已有VS Code这个软件的基础上,数据库举的例子是MySQL 1、打开扩展 2、安装MySQL插件 在搜索框搜索 MySQL和 MySQL Syntax,下载这三个插件 点击下面的插件,选择【install】安装

By Ne0inhk
Linux 系统 MySQL 完整安装配置教程:从卸载 MariaDB 到优化 my.cnf----《Hello MySQL!》(1)

Linux 系统 MySQL 完整安装配置教程:从卸载 MariaDB 到优化 my.cnf----《Hello MySQL!》(1)

文章目录 * 前言 * 卸载不要的环境 * 检查系统安装包 * 卸载这些包 * 安装MySQL官方yum源 * 使用程序 * 配置my.cnf * 引申:一些其他的指令 前言 在 Linux 系统中,许多发行版(如 CentOS 7)默认预装 MariaDB(MySQL 的分支项目),但在实际开发、部署场景中,我们常需要安装 MySQL 官方版本以满足特定兼容性或功能需求。然而,新旧数据库环境的冲突、YUM 源配置异常、初始密码登录、中文乱码等问题,往往成为新手入门的 “绊脚石”。 本文将从卸载冗余环境出发,一步步带你完成 MySQL 官方 YUM 源配置、服务器安装、服务启停、客户端登录(含 3 种密码解决方案)、核心配置文件(my.

By Ne0inhk