目录
图像渲染
题意:
题解:
非递归:
递归:
岛屿数量
题解:
非递归:
递归:
岛屿的最大面积
题解:
非递归:
递归:
被围绕的区域
题解:
非递归:
递归:
图像渲染
733. 图像渲染 - 力扣(LeetCode)https://leetcode.cn/problems/flood-fill/description/
题意:
把初始位置的颜色作为标准值,如果其上下左右位置的颜色和标准值相同,就把该位置的颜色更改为 newColor,接着继续往外扩,重复上述操作。
题解:
这种题的思想类似树的层序遍历,我们从指定的坐标出发,查看上下左右的颜色,相当于查看外面一层的颜色,把需要染色的位置染色后,继续查看该位置外面一层的颜色。
思想类似,代码也类似。
我们定义一个队列,队列中存储需要修改颜色的位置的下标,便于层序遍历,先把初始位置入队:
1、取出队头 t ,并删除队头,接着修改 t 的颜色;
2、判断 t 的上、下、左、右是否需要修改颜色,如果需要修改,则把对应的位置入队;
3、在数组中,假设 t 的下标为 (x,y),那么其上下左右的下标如图:
为了便于访问上下左右的位置,我们定义 dx、dy 数组,dx、dy 的同一个位置,对应着相对于 t 下标的偏移量,比如 t 的右边,可以看作 x 位置不变(相当于偏移了 0 个单位),y 向右偏移 1 个单位(+1),t 的左边,可以看作 x 位置不变,y 向左偏移 1 个单位(-1)。
int dx[4]={0,0,1,-1};//分别对应右、左、下、上
int dy[4]={1,-1,0,0};
我们只需要用 for 循环同时遍历这两个数组,就可以获得偏移量来计算上下左右的下标,但是这样的计算存在数组越界的风险,所以计算后的结果需要判断是否越界!
该算法会对同一个位置重复访问:
1、对于不需要修改颜色的位置,即使多次访问也不会修改颜色,并不影响最终结果;
2、对于需要修改颜色的位置,第一次访问时,已经更改颜色,所以颜色和 std 不一样了,后序再次访问,已经变成情况 1 了,不会再次更改颜色。
非递归:
class Solution {
public:
vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {
int std=image[sr][sc];
if(std==color) return image;
int dx[4]={0,0,1,-1};//分别对应右、左、下、上
int dy[4]={1,-1,0,0};
int m=image.size(),n=image[0].size();
queue<pair<int,int>> q;
q.push({sr,sc});
while(!q.empty())
{
auto [x,y]=q.front();
q.pop();
image[x][y]=color;//修改像素
for(int i=0;i<4;i++)
{
int x1=x+dx[i],y1=y+dy[i];//存入上下左右
if(x1>=0 && x1<m && y1>=0 && y1<n && image[x1][y1]==std)
{
q.push({x1,y1});
}
}
}
return image;
}
};
递归:
递归算法相当于在找需要修改颜色的下标时,一直向同一个方向递归,可以是一直向左、一直向右、一直向下、一直向上递归,直到这个颜色是不需要修改时,不再递归,再去判断其他方向是否需要修改颜色。
class Solution {
public:
vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int color) {
if(image[sr][sc]==color) return image;
int std=image[sr][sc];
dfs(image,sr,sc,color,std);
return image;
}
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
void dfs(vector<vector<int>>& image, int i, int j, int color,int std)
{
//修改当前格
image[i][j]=color;
for(int k=0;k<4;k++)
{
int x=i+dx[k],y=j+dy[k];
//修改上下左右
if(x>=0 && x<image.size() && y>=0 && y<image[0].size() && image[x][y] ==std)
dfs(image,x,y,color,std);
}
}
};
岛屿数量
200. 岛屿数量 - 力扣(LeetCode)https://leetcode.cn/problems/number-of-islands/description/
题解:
图像渲染是一个点不断向外扩展,这道题是多个点不断向外扩展,需要记录一共有多少个点源向外扩展了。
我们用两层 for 循环遍历数组,如果遍历到的位置是陆地,由于题目定义岛屿被水围绕,所以我们需要不断扩展,直到周围都是水,才停止扩展,此时岛屿数量+1。图像渲染即使对同一个位置重复访问也不会影响结果,而这道题同一个位置重复访问,结果会出现很大的错误!
以示例 1 为例,如左图,当我们访问 grid[ 0 ][ 0 ] 时,该位置为陆地,我们不断扩展,岛屿数量+1,当我们访问 grid[ 0 ][ 1 ] 时,该位置是陆地,但当前访问的陆地和上一次访问的陆地属于同一座岛屿,不能被认为是新的岛屿了!
我们设置一个和 grid 同等规模的 bool 类型的数组 vis,用于标记该陆地是否为未被发现的岛屿。
1、当 grid[ i ][ j ] 为陆地时,查看 vis[ i ][ j ] 的bool 值:
a. vis[ i ][ j ] 为 true,是已经被发现的岛屿,该陆地不可以被记为岛屿;
b. vis[ i ][ j ] 为 false,是未被发现的岛屿,可以记为新的岛屿,并且向外扩展,把相连的陆地的 vis 全部记为 true,直到周围都是水为止。
2、当 grid[ i ][ j ] 为水时,不需要做任何处理。
扩展的思路和图像渲染一样,这里不再赘述。
非递归:
class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
int m=grid.size(),n=grid[0].size();
vector<vector<bool>> vis(m,vector<bool>(n));
int ret=0;
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(grid[i][j]=='1' && !vis[i][j])
{
ret++;
bfs(grid,vis,i,j,m,n);
}
}
}
return ret;
}
void bfs(vector<vector<char>>& grid,vector<vector<bool>>& vis,int i,int j,int m,int n)
{
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
queue<pair<int,int>> q;
q.push({i,j});
while(!q.empty())
{
auto [a,b]=q.front();
q.pop();
for(int k=0;k<4;k++)
{
int x=a+dx[k],y=b+dy[k];
if(x>=0 && x<m && y>=0 && y<n && grid[x][y]=='1' && !vis[x][y])
{
q.push({x,y});
vis[x][y]=true;
}
}
}
}
};
递归:
class Solution {
public:
int numIslands(vector<vector<char>>& grid) {
vector<vector<bool>> vis(grid.size(),vector<bool> (grid[0].size()));
int ret=0;
for(int i=0;i<grid.size();i++)
{
for(int j=0;j<grid[0].size();j++)
{
if(grid[i][j]=='1' && !vis[i][j])
{
++ret;
dfs(grid,vis,i,j);
}
}
}
return ret;
}
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
void dfs(vector<vector<char>>& grid,vector<vector<bool>>& vis,int i,int j)
{
vis[i][j]=true;//修改当前格
for(int k=0;k<4;k++)
{
int x=i+dx[k],y=j+dy[k];
if(x>=0 && x<grid.size() && y>=0 && y<grid[0].size() && grid[x][y]=='1'&& !vis[x][y] )
dfs(grid,vis,x,y);
}
}
};
岛屿的最大面积
695. 岛屿的最大面积 - 力扣(LeetCode)https://leetcode.cn/problems/max-area-of-island/description/
题解:
这道题只需要在每次扩展岛屿的陆地时,记录一共扩展了多少块陆地即可,同理对已经被发现的陆地,不可以重复计算。
非递归:
class Solution {
public:
int maxAreaOfIsland(vector<vector<int>>& grid) {
int ret=0,m=grid.size(),n=grid[0].size();
vector<vector<bool>> vis(m,vector<bool>(n));
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(grid[i][j]==1 && !vis[i][j])
{
int tmp=AreaOfIsland(grid,i,j,vis);
ret=max(ret,tmp);
}
}
}
return ret;
}
int AreaOfIsland(vector<vector<int>>& grid,int i,int j,vector<vector<bool>>& vis)
{
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
queue<pair<int,int>> q;
q.push({i,j});
int area=1;
//++area;
vis[i][j]=true;
while(!q.empty())
{
auto [a,b]=q.front();
q.pop();
for(int k=0;k<4;k++)
{
int x=a+dx[k],y=b+dy[k];
if(x>=0 && x<grid.size() && y>=0 && y<grid[0].size() && grid[x][y]==1 && !vis[x][y])
{
q.push({x,y});
vis[x][y]=true;
++area;
}
}
}
return area;
}
};
递归:
class Solution {
public:
int maxAreaOfIsland(vector<vector<int>>& grid) {
vector<vector<bool>> vis(grid.size(),vector<bool>(grid[0].size()));
int ret=0;
for(int i=0;i<grid.size();i++)
{
for(int j=0;j<grid[0].size();j++)
{
if(grid[i][j]==1 && !vis[i][j])
{
int tmp=0;
dfs(grid,vis,i,j,tmp);
ret=max(ret,tmp);
}
}
}
return ret;
}
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
void dfs(vector<vector<int>>& grid,vector<vector<bool>>& vis,int i,int j,int& area)
{
++area;
vis[i][j]=true;//修改当前值
for(int k=0;k<4;k++)
{
int x=i+dx[k],y=j+dy[k];
if(x>=0 && x<grid.size() && y>=0 && y<grid[0].size() && grid[x][y]==1 && !vis[x][y])
dfs(grid,vis,x,y,area);
}
}
};
被围绕的区域
130. 被围绕的区域 - 力扣(LeetCode)https://leetcode.cn/problems/surrounded-regions/description/
题解:
本道题和图像渲染类似,但是如果区域内的 O 与边界的 O 相连,那么这个 O 不可以被修改!这个特殊条件并不好处理。
1、我们可以优先处理和边界相连的 O:
可以设置一个和 board 相同规模的 bool类型的数组 vis,把和边界相连的 O 的对应位置的 vis 设置为 true。
2、用两层 for 循环遍历 board:
a. 如果 board[ i ][ j ] 为 O,且 vis[ i ][ j ] 为 false,说明这个 O 没有和边界的 O 相连,把它修改为 X;
b. 如果 board[ i ][ j ] 为 O,且 vis[ i ][ j ] 为 true,说明这个 O 和边界的 O 相连,不需要修改;
c. 如果 board[ i ][ j ] 为 X,不需要做任何处理。
非递归:
class Solution {
public:
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int m,n;
void solve(vector<vector<char>>& board) {
m=board.size(),n=board[0].size();
vector<vector<bool>> vis(m,vector<bool>(n));
for(int i=0;i<n;i++)
{
if(board[0][i]=='O' && !vis[0][i])//第一行
SetVisO(board,vis,0,i);
if(board[m-1][i]=='O' && !vis[m-1][i])//最后一行
SetVisO(board,vis,m-1,i);
}
for(int i=1;i<m-1;i++)
{
if(board[i][0]=='O' && !vis[i][0])//第一列
SetVisO(board,vis,i,0);
if(board[i][n-1]=='O' && !vis[i][n-1])//最后一列
SetVisO(board,vis,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';
}
}
}
//处理边界的O
void SetVisO(vector<vector<char>>& board,vector<vector<bool>>& vis,int i,int j)
{
queue<pair<int,int>> q;
q.push({i,j});
vis[i][j]=true;//处理当前位置
while(!q.empty())
{
auto [a,b]=q.front();
q.pop();
for(int k=0;k<4;k++)//处理上下左右位置
{
int x=a+dx[k],y=b+dy[k];
if(x>=0 && x<m && y>=0 && y<n && board[x][y]=='O' && !vis[x][y])
{
vis[x][y]=true;
q.push({x,y});
}
}
}
}
};
递归:
class Solution {
public:
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int m,n;
void solve(vector<vector<char>>& board) {
m=board.size(),n=board[0].size();
vector<vector<bool>> vis(m,vector<bool>(n));
for(int i=0;i<n;i++)
{
if(board[0][i]=='O' && !vis[0][i])//第一行
SetVisO(board,vis,0,i);
if(board[m-1][i]=='O' && !vis[m-1][i])//最后一行
SetVisO(board,vis,m-1,i);
}
for(int i=1;i<m-1;i++)
{
if(board[i][0]=='O' && !vis[i][0])//第一列
SetVisO(board,vis,i,0);
if(board[i][n-1]=='O' && !vis[i][n-1])//最后一列
SetVisO(board,vis,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])//根据O的状态来判断是否修改
board[i][j]='X';
}
}
}
//处理边界的O
void SetVisO(vector<vector<char>>& board,vector<vector<bool>>& vis,int i,int j)
{
vis[i][j]=true;
for(int k=0;k<4;k++)
{
int x=i+dx[k],y=j+dy[k];
if(x>=0 && x<board.size() && y>=0 && y<vis[0].size() && board[x][y]=='O'&&!vis[x][y])
SetVisO(board,vis,x,y);
}
}
};