本节目标:
1 . 了解BFS 、Floodfill概念
2 . 通过这个简易例子,贯通概念并小练一下代码能力
题目介绍
在给定的 m x n 网格 grid 中,每个单元格可以有以下三个值之一:
- 值
0代表空单元格; - 值
1代表新鲜橘子; - 值
2代表腐烂的橘子。
每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。
返回 直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1 。

示例 2:
cpp
输入:grid = [[2,1,1],[0,1,1],[1,0,1]]
输出:-1
解释:左下角的橘子(第 2 行, 第 0 列)永远不会腐烂,因为腐烂只会发生在 4 个方向上。
示例 3:
cpp
输入:grid = [[0,2]]
输出:0
解释:因为 0 分钟时已经没有新鲜橘子了,所以答案就是 0 。
提示:
m == grid.lengthn == grid[i].length1 <= m, n <= 10grid[i][j]仅为0、1或2
cpp
class Solution {
public:
int orangesRotting(vector<vector<int>>& grid) {
}
};
本文约 4500字,阅读+思考约16 min------FloodFill初篇
检验答案网址:994. 腐烂的橘子 - 力扣(LeetCode)
解析
1 . 本题需求很明确:给你一个二维数组,里面的每个格子有三种可能:
a . 为1 ,新鲜的橘子。可能被感染
b . 为2 ,腐烂的橘子。作为感染的起点(感染新鲜橘子的起点)
c . 为0 ,无橘子。
2 . 问题从:计算出整箱橘子是否会腐烂完全进行判断------如果腐烂完全,计算出腐烂的时间(min) ;反之,则返回-1
3 . 转化为:FloodFill问题
概念补充
1 . 什么是BFS?什么又是FloodFill?
a . Breadth-First Search(广度优先搜索) ,是一种遍历 / 搜索算法 ,核心规则是:先访问离起点最近的节点,再访问次近的,层层向外扩散。
b . BFS------常称为层序遍历。"依次访问最近的结点",什么意思。不妨举个二叉树例子:
i ) "最近的结点" = 和根节点距离(层数)相同的结点,比如根节点是第 0 层,它的左右孩子是第 1 层,这些孩子就是 "最近的结点";
ii ) "依次访问" = 先访问完当前层的所有结点,再访问下一层的结点,绝不跳过当前层去访问更远的结点。

c . BFS遍历的结果:第 0 层(1)→ 第 1 层(2、3)→ 第 2 层(4、5、6)
2 . FloodFill(泛洪填充)是一种应用场景 / 问题类型,不是算法本身3 . 它描述的是:从一个 / 多个起点出发,按照特定规则(如颜色相同、值相等),向四周扩散,将符合条件的区域 "填充" 为目标状态。
4 . 理解看来:BFS是一种算法,而FloodFill是一种可以基于BFS这种算法的问题实例
BFS_FloodFill
1 . 感染源:是刚开始数组里所有的烂橘子(grid[ i ][ j ] == 2)
2 . 在每1分钟内,感染源的四周(左 、右 、上 、下)如果:
有新鲜橘子就会被感染为烂橘子;
无新鲜橘子,或无橘子,则无事
3 . 接着,烂橘子继续扩散污染
4 . 这已经接近FloodFill底层的逻辑------本次是从多个污染源出发,如果四周存在值为1的格子,那么对该区域修改为'2'
5 . 聊聊代码实现。a . BFS:层序遍历 ------ 自然想到得天独厚的FIFO(先进先出)结构:队列
b . 离得近先出来,不就是FIFO?
c . 根据现在的代码逻辑:先把箱子里所有的2统计出来,作为污染源
cpp
class Solution {
public:
int orangesRotting(vector<vector<int>>& grid) {
int m = grid.size(),n = grid[0].size();// 1 <= m , n <= 10;
std::queue<pair<int,int>> q;//存储污染源
for(int i = 0;i < m;i++)
{
for(int j = 0;j < n;j++)
{
if(grid[i][j] == 2)
{
grid[i][j] = -1;//污染源已经存进去,打个标记
q.push({i,j});//把值为2的下标push进队列
}
}
}
}
};
d . 然后开始提取出第0分钟的所有污染源,让其扩散自己的四周
i ) 扩散四周------还得制定一个方向数组
ii ) 第0分钟的所有污染源数量,即q的现有大小
cpp
std::vector<pair<int,int>> direction = {{0,-1},{0,1},{1,0},{-1,0}};
while(true)
{
int bad_source = q.size();
for(int i = 0;i < bad_source;i++)
{
auto [x,y] = q.front();q.pop();
//x,y为当前bad_source之一,执行污染其他新鲜橘子的逻辑
// ......直到所有污染源污染一次后,才是第0分钟后污染的结果
}
}
6 . 我们相信,在每一次bad_cource之一污染四周时,会导入新的污染源。旧污染源因为污染范围有限------污染一次后就自然pop()掉
7 . 当退出while循环,我们需要得知当前grid(箱子)里,是否存活新鲜橘子------很明显,我们还需要记录新鲜橘子的个数:
cpp
class Solution {
public:
int orangesRotting(vector<vector<int>>& grid) {
int m = grid.size(),n = grid[0].size();// 1 <= m , n <= 10;
std::queue<pair<int,int>> q;
int fresh = 0;// 新鲜橘子个数
for(int i = 0;i < m;i++)
{
for(int j = 0;j < n;j++)
{
if(grid[i][j] == 2)
{
grid[i][j] = -1;
q.push({i,j});
}
else if(grid[i][j] == 1)
++fresh;// ++
}
}
std::vector<pair<int,int>> direction = {{0,-1},{0,1},{1,0},{-1,0}};
while(true)
{
int bad_source = q.size();
for(int i = 0;i < bad_source;i++)
{
//
}
}
if(fresh)//当退出所有污染源进行污染后,检查箱子里剩余的fresh个数
return -1;
return //显然,还需要记录污染用时
}
};
8 . 添加mininue作为总的污染用时:
cpp
class Solution {
public:
int orangesRotting(vector<vector<int>>& grid) {
int m = grid.size(),n = grid[0].size();// 1 <= m , n <= 10;
std::queue<pair<int,int>> q;
int fresh = 0;
int minute = 0;// 记录总的污染用时
for(int i = 0;i < m;i++)
{
for(int j = 0;j < n;j++)
{
if(grid[i][j] == 2)
{
grid[i][j] = -1;
q.push({i,j});
}
else if(grid[i][j] == 1)
++fresh;
}
}
std::vector<pair<int,int>> direction = {{0,-1},{0,1},{1,0},{-1,0}};
while(true)
{
int bad_source = q.size();
for(int i = 0;i < bad_source;i++)
{
//TO DO
}
// for循环结束,表明本层的所有污染源污染动作结束------正是1min内污染达到的效果
++minute;
}
if(fresh)
return -1;
return minute;
}
};
9 . 已知:本层所有的污染源个数,以及每个污染源的坐标------那,只要明白一个污染源的污染逻辑,就可以循环所有污染源执行相同操作
cpp
std::vector<pair<int,int>> direction = {{0,-1},{0,1},{1,0},{-1,0}};
while(true) // ?
{
int bad_source = q.size();
for(int i = 0;i < bad_source;i++)
{
auto [x,y] = q.front();//取出当前污染源,开始四个方向污染
for(int d = 0;d < 4;d++)
{
int curr_x = x+direction[d].first;
int curr_y = y+direction[d].second;
if(curr_x < 0 || curr_x >= m || curr_y < 0 || curr_y >= n)
continue;//如果当前没有上格子,或者某个方向格子。跳过该位置
else if(grid[curr_x][curr_y] == 1)
{
--fresh;
grid[curr_x][curr_y] = 2;//它现在坏了
q.push({curr_x,curr_y});//成为新的污染源
}
// else if grid[curr_x][curr_y] == 2或者==-1?不用管,只污染新鲜橘子
}
}
}
10 . 所以while的循环条件是什么呢?当队列里的污染源全都进行了污染动作,那么bad_source不再需要执行污染逻辑
还有,当新鲜果子==0也没有执行污染逻辑的必要
cpp
while(!q.empty() && fresh > 0)
{
int bad_source = q.size();
for(int i = 0;i < bad_source;i++)
{
auto [x,y] = q.front();//取出当前污染源,开始四个方向污染
for(int d = 0;d < 4;d++)
{
int curr_x = x+direction[d].first;
int curr_y = y+direction[d].second;
if(curr_x < 0 || curr_x >= m || curr_y < 0 || curr_y >= n)
continue;//如果当前没有上格子,或者某个方向格子。跳过该位置
else if(grid[curr_x][curr_y] == 1)
{
--fresh;
grid[curr_x][curr_y] = 2;//它现在坏了
q.push({curr_x,curr_y});//新的污染源
}
// else if grid[curr_x][curr_y] == 2或者==-1?不用管,只污染新鲜橘子
}
}
// for循环结束,表明本层的所有污染源污染动作结束------正是1min内污染达到的效果
++minute;
}
那么完整的代码:
cpp
class Solution {
public:
int orangesRotting(vector<vector<int>>& grid) {
int m = grid.size(),n = grid[0].size();// 1 <= m , n <= 10;
std::queue<pair<int,int>> q;
int fresh = 0;
int minute = 0;
for(int i = 0;i < m;i++)
{
for(int j = 0;j < n;j++)
{
if(grid[i][j] == 2)
{
grid[i][j] = -1;
q.push({i,j});
}
else if(grid[i][j] == 1)
++fresh;
}
}
std::vector<pair<int,int>> direction = {{0,-1},{0,1},{1,0},{-1,0}};
while(q.size() && fresh > 0)
{
int bad_source = q.size();
for(int i = 0;i < bad_source;i++)
{
auto [x,y] = q.front();q.pop();
for(int d = 0;d < 4;d++)
{
int curr_x = x+direction[d].first;
int curr_y = y+direction[d].second;
if(curr_x < 0 || curr_x >= m || curr_y < 0 || curr_y >= n)
continue;
else if(grid[curr_x][curr_y] == 1)
{
--fresh;
grid[curr_x][curr_y] = -1;
q.push({curr_x,curr_y});
}
}
}
++minute;
}
if(fresh)
return -1;
return minute;
}
};
总结以及完整参考代码

cpp
class Solution {
public:
int orangesRotting(vector<vector<int>>& grid) {
int m = grid.size(),n = grid[0].size();// 1 <= m , n <= 10;
std::queue<pair<int,int>> q;
int fresh = 0;
int minute = 0;
for(int i = 0;i < m;i++)
{
for(int j = 0;j < n;j++)
{
if(grid[i][j] == 2)
{
grid[i][j] = -1;
q.push({i,j});
}
else if(grid[i][j] == 1)
++fresh;
}
}
std::vector<pair<int,int>> direction = {{0,-1},{0,1},{1,0},{-1,0}};
while(q.size() && fresh > 0)
{
int bad_source = q.size();
for(int i = 0;i < bad_source;i++)
{
auto [x,y] = q.front();q.pop();
for(int d = 0;d < 4;d++)
{
int curr_x = x+direction[d].first;
int curr_y = y+direction[d].second;
if(curr_x < 0 || curr_x >= m || curr_y < 0 || curr_y >= n)
continue;
else if(grid[curr_x][curr_y] == 1)
{
--fresh;
grid[curr_x][curr_y] = -1;
q.push({curr_x,curr_y});
}
}
}
++minute;
}
if(fresh)
return -1;
return minute;
}
};
Happy Valentine's Day
祝大家情人节也学业进步、工作顺利