[Lc15_bfs+floodfill] 图像渲染 | 岛屿数量 | 岛屿的最大面积 | 被围绕的区域
目录
1.图像渲染
题解
2.岛屿数量
题解
3.岛屿的最大面积
题解
4.被围绕的区域
题解
FloodFill 算法 就是在一个矩阵中找性质相同的连通块。
- 关于这个后面会在《递归、搜索回溯专题》 详细介绍。
- 并且用的是DFS解决问题,这里主要是用BFS解决FloodFill 算法。
1.图像渲染
链接:733. 图像渲染
有一幅以 m x n
的二维整数数组表示的图画 image
,其中 image[i][j]
表示该图画的像素值大小。你也被给予三个整数 sr
, sc
和 color
。你应该从像素 image[sr][sc]
开始对图像进行上色 填充 。
为了完成 上色工作:
- 从初始像素开始,将其颜色改为
color
。 - 对初始坐标的 上下左右四个方向上 相邻且与初始像素的原始颜色同色的像素点执行相同操作。
- 通过检查与初始像素的原始颜色相同的相邻像素并修改其颜色来继续 重复 此过程。
- 当 没有 其它原始颜色的相邻像素时 停止 操作。
最后返回经过上色渲染 修改 后的图像 。
题解
这道题的意思是,给一个开始位置,把和它性质相同的 连通块都找到,并且渲染成color。
注意可以 上下左右 四个方向去找。
int dx[4] = {0, 0, 1, -1};
int dy[4] = {1, -1, 0, 0};
//....
//实现 上下左右四个方向 的移动
for(int i = 0; i < 4; ++i)
int x = a + dx[i], y = b + dy[i];
解法:BFS
BFS(宽度优先遍历/宽度优先搜索/层序遍历),借助队列实现,一层一层出。
- 起始位置先入队。
- 队列不空出队,把它上下左右和它颜色相同的放入队列。
- 然后循环下去,直到队列为空。
class Solution {
public:
//在queue中对pair处理一下
typedef pair<int,int> PII;
//bfs
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
vector<vector<int>> floodFill
(vector<vector<int>>& image, int sr, int sc, int color)
{
queue<PII> q;
if(image[sr][sc]==color) return image;
//record init color
int prev=image[sr][sc];
q.push({sr,sc});
image[sr][sc] = color; // 入队后 着色
int x_sz=image.size();
int y_sz=image[0].size();
while(q.size())
{
auto [a,b]=q.front();
q.pop();
for(int i=0;i<4;i++)
{
int x=a+dx[i],y=b+dy[i];
if(x<x_sz && y<y_sz && x>=0 && y>=0
&& image[x][y]==prev)
{
q.push({x,y});
image[x][y]=color;
}
}
}
return image;
}
};
2.岛屿数量
链接:200. 岛屿数量
给你一个由 '1'
(陆地)和 '0'
(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
题解
求岛屿的数量,每座岛屿只能上下左右去找。
解法:BFS
- 从左往右扫描这个矩阵,当遇到陆地的时候就把和陆地相连的岛屿找到
- 如何找,就是BFS从这个地方宽搜一遍,就可以把连通的区域找到。
- 可以用一个变量ret 记录陆地数量就可以。
但是这里层序遍历有一个问题。从一个位置扩展到下一个位置
不能在从下一个位置在回到上一个位置不然就死循环了。
为了避免这种情况我们有两种方式。
1. 修改原始矩阵的值
2. 用一个同等矩阵大小的bool类型的二维数组,记录当前位置是否已经被搜索过
- false表示没有,true表示搜索过。
- 并且当前位置bfs一次之后。和它相连的其他岛屿也被置为true
- 然后从左往右遍历的时候即使它是1,也不会进去了。
3.岛屿的最大面积
链接:695. 岛屿的最大面积
给你一个大小为 m x n
的二进制矩阵 grid
。
岛屿 是由一些相邻的 1
(代表土地) 构成的组合,这里的「相邻」要求两个 1
必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid
的四个边缘都被 0
(代表水)包围着。
岛屿的面积是岛上值为 1
的单元格的数目。
计算并返回 grid
中最大的岛屿面积。如果没有岛屿,则返回面积为 0
。
题解
这道题是求那 一个连通块的面积是最大的。
解法:BFS
还是从左往右扫完,当碰到一块土地就去找和这个土地连通的所有土地,如何找?
还是用BFS遍历一次,同时要有一个变量count统计这个岛屿面积有多大。
- 注意找过的土地下一次不能在找了!
- 因此我们还是来一个bool类型的二维数组来标记那些土地是已经被搜索过了!
class Solution {
public:
typedef pair<int,int> PII;
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int row,col;
vector<vector<bool>> vis;
int ret=0;
int maxAreaOfIsland(vector<vector<int>>& grid)
{
if(grid.empty()) return 0;
row=grid.size();
col=grid[0].size();
vis.resize(row,vector<bool>(col,false));
for(int i=0;i<row;i++)
{
for(int j=0;j<col;j++)
{
if(grid[i][j]==1 && !vis[i][j])
{
ret=max(ret,bfs(grid,i,j));
}
}
}
return ret;
}
int bfs(vector<vector<int>>& grid,int i,int j)
{
queue<PII> q;
q.push({i,j});
vis[i][j]=true;
int count=1;
while(q.size())
{
auto [a,b]=q.front();
q.pop();
for(int m=0;m<4;m++)
{
int x=a+dx[m],y=b+dy[m];
if(x>=0 && x<row && y>=0 && y<col
&& grid[x][y]==1 && !vis[x][y])
{
q.push({x,y});
vis[x][y]=true;
count++;
}
}
}
return count;
}
};
4.被围绕的区域
链接: 130. 被围绕的区域
给你一个 m x n
的矩阵 board
,由若干字符 'X'
和 'O'
组成,捕获 所有 被围绕的区域:
- 连接:一个单元格与水平或垂直方向上相邻的单元格连接。
- 区域:连接所有
'O'
的单元格来形成一个区域。 - 围绕:如果您可以用
'X'
单元格 连接这个区域,并且区域中没有任何单元格位于board
边缘,则该区域被'X'
单元格围绕。
通过 原地 将输入矩阵中的所有 'O'
替换为 'X'
来 捕获被围绕的区域。你不需要返回任何值。
题解
- 把被 ’ X ’ 围绕的 ’ O '区域修改为 ’ X '
- 被围绕的区域指的是 这个区域的 ’ O ’ 没有处于边界。
如下面这张图只有一处区域是要被修改的,此时我们是有两种解法的。
解法一:直接做
- 从左往右扫描,当遇到 ‘O’ 之后,就把与这个 ‘O’ 相连的 ‘O’ 区域都修改成 ‘X’,这是我们之前做的方式。
- 但是这里有一个问题当我在扫描遇到 ‘O’ 也会把下面绿色区域修改成 ‘X’ ,但是这块区域有 ‘O’ 是处于边界的,因此这一块区域是不能够被修改的!
- 所以在BFS过程中遇到 ‘O’ 处于边界的情况这块是不能被修改的。
- 更为极端的例子是,当BFS是把这个区域遍历一遍之后才知道这个区域是不能够被修改的,还要把已经被修改过成 ‘X’ 还原成 ‘O’。
- 但是我们BFS是一边修改一边遍历的。然后遍历到 ‘O’ 处于边界 才知道这个区域是不能被修改的
- 并且刚才修改过的是错的,还要还原回去,但此时前面变成 ‘X’ 的 ‘O’ 特别难被还原回去。
不过还可以这样,先去BFS遍历一遍看看这个区域是否合法
如果合法在去BFS把这块区域修改成 ‘X’,需要两个BFS。
解法二:正难则反
- 正着来不太好处理与边界相连的连通块,那就 反着来就先处理与边相连的连通块。
- 先遍历四条边界,在四条边界发现有 ‘O’ ,仅对这些边界上的 ‘O’ 来一次BFS,把这个连通块都修改成 ‘.’。(引入 变化中间量,好来之后 if 进行处理)
- 这样把处于边界的 ‘O’ 连通快都修改成 '.'之后,接下来仅需遍历一下矩阵,把矩阵中的剩下的 ‘O’ 修改成 ‘X’
把 ‘.’ 还原成 'O’就可以了。
- 先处理边界上 ‘O’ 区域
- 扫描矩阵,还原即可