[Lc5_dfs+floodfill] 岛屿的最大面积(传参) | 被围绕的区域 | 太平洋大西洋水流问题(双标记位传参)
目录
1.岛屿的最大面积
题解
2.被围绕的区域
题解
3.太平洋大西洋水流问题
题解
1.岛屿的最大面积
链接:695. 岛屿的最大面积
给你一个大小为 m x n
的二进制矩阵 grid
。
岛屿 是由一些相邻的 1
(代表土地) 构成的组合,这里的「相邻」要求两个 1
必须在 水平或者竖直的四个方向上 相邻。你可以假设 grid
的四个边缘都被 0
(代表水)包围着。
岛屿的面积是岛上值为 1
的单元格的数目。
计算并返回 grid
中最大的岛屿面积。如果没有岛屿,则返回面积为 0
。
题解
上面是让找岛屿数量,这里让找岛屿面积最大,思想几乎是一模一样
- 我们用的肯定还是深度优先遍历,此时依旧是一行一行扫描,当扫描到一个陆地之后就由这个陆地开始来一次深度优先遍历
- 但是此时做深度优先遍历我们可以搞一个变量count 全局和参数都可以,只要进入一次dfs,就让count++。
- 当这个深度优先遍历结束之后,此时这个count就是统计这个岛屿的面积。
然后让一个ret统计 更新为所有count里面的最大值,就可以把最大岛屿面积找到了。
class Solution {
public:
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int ret=0,m,n;
vector<vector<bool>> check;
int maxAreaOfIsland(vector<vector<int>>& grid)
{
m=grid.size();
n=grid[0].size();
check.resize(m,vector<bool>(n,false));
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(!check[i][j] && grid[i][j])
{
check[i][j]=true;
//传& curr,好dfs后,进行比较
int curr=1;
dfs(grid,i,j,curr);
ret=max(ret,curr);
}
}
}
return ret;
}
void dfs(vector<vector<int>>& grid,int i,int j,int& count)
{
for(int k=0;k<4;k++)
{
int x=i+dx[k],y=j+dy[k];
if(x>=0 && x<m && y>=0 && y<n
&& !check[x][y] && grid[x][y])
{
check[x][y]=true;
count++;
dfs(grid,x,y,count);
}
}
}
};
在主函数中设置 curr 传& ,好dfs后,进行比较
- int curr=1;
- dfs(grid,i,j,curr);
- ret=max(ret,curr);
2.被围绕的区域
链接:130. 被围绕的区域
给你一个 m x n
的矩阵 board
,由若干字符 'X'
和 'O'
组成,捕获 所有 被围绕的区域:
- 连接:一个单元格与水平或垂直方向上相邻的单元格连接。
- 区域:连接所有
'O'
的单元格来形成一个区域。 - 围绕:如果您可以用
'X'
单元格 连接这个区域,并且区域中没有任何单元格位于board
边缘,则该区域被'X'
单元格围绕。
通过 原地 将输入矩阵中的所有 'O'
替换为 'X'
来 捕获被围绕的区域。你不需要返回任何值。
题解
把被X上下左右 被围绕的O区域变成X
- 注意这个O不能处于边界
- 因为处于边界的O,并不会被X上下左右围绕。
解法一:直接去做(有困难)
因为涉及到一个要是发现没有被围绕,还要还原的问题
解法二:正难则反
如果直接去搞比较难,我可以反着来!
- 这个问题与边界相连的O区域比较难搞
- 此时就能先把处于边界区域的O先处理一下。那剩下的O就自然在内部了。
那边界怎么处理呢?
- 我们只要扫描边界就可以了,当碰到O就从这个位置来一次dfs,可以把与这个位置相连的O位置标记一下。
- 标记有两种策略,一:搞一个vis数组。二:修改原始值 把边界这些 O 修改成 . 。
- 然后把边界情况处理完之后,在扫描整个矩阵当碰到 . 就把它还原成一个 O,当碰到 O 修改成 X。
这样就完美的解决这个问题了。
(其实感觉无论BFS还是DFS,都是一种暴力的搜索,借助数据结构来实现,然后对搜索结果进行一个标记,之后获取)
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();
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if((i==0 || i==m-1 || j==0 ||
j==n-1)&& board[i][j]=='O')
//正难则反的查找,边界上的O
{
board[i][j]='.';
dfs(board,i,j);
}
}
} //找完都变为.
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(board[i][j]=='O')
board[i][j]='X';
else if(board[i][j]=='.')
board[i][j]='O';
}
}
}
void dfs(vector<vector<char>>& board,int i,int j)
{
for(int k=0;k<4;k++)
{
int x=i+dx[k],y=j+dy[k];
if(x>=0 && x<m && y>=0 && y<n
&& board[x][y]=='O')
{
board[x][y]='.';
dfs(board,x,y);
}
}
}
};
使包围区域填充算法正确处理边界O
- dfs 中调用 dfs(grip,x,y),不是再传 i,j 了,要细心!
3.太平洋大西洋水流问题
链接:417. 太平洋大西洋水流问题
有一个 m × n
的矩形岛屿,与 太平洋 和 大西洋 相邻。 “太平洋” 处于大陆的左边界和上边界,而 “大西洋” 处于大陆的右边界和下边界。
这个岛被分割成一个由若干方形单元格组成的网格。给定一个 m x n
的整数矩阵 heights
, heights[r][c]
表示坐标 (r, c)
上单元格 高于海平面的高度 。
岛上雨水较多,如果相邻单元格的高度 小于或等于 当前单元格的高度,雨水可以直接向北、南、东、西流向相邻单元格。水可以从海洋附近的任何单元格流入海洋。
返回网格坐标 result
的 2D 列表 ,其中 result[i] = [ri, ci]
表示雨水从单元格 (ri, ci)
流动 既可流向太平洋也可流向大西洋 。
题解
题目描述很难懂,这里我们简单说一下。
- 有一个mxn的矩阵,这个矩阵相当于一个岛屿。
- 这个岛屿被两个洋包围
- 其中上和左被太平洋包围。下和右被大西洋包围。
这个岛屿被分成一块一块的。其中数字代表这一块的高度。
- 此时如果有水的话,水是可以从较高的地方流向较低的地方,还可以流向和在我周围并且和我相等高度的地方。
- 注意只能上下左右流动。
- 然后题目要求在所有的小格子里能否存在一个位置,这个位置的水既可以流向太平洋又可以流向大西洋。
- 如果可以就把这个位置坐标存下来,最后返回。
- 最终找到的就是这个岛屿中所有即可流向太平洋又可以流向大西洋的小格子。
把题目搞懂了,你可能里面会想到把所有小格子枚举一遍。
- 遇到一个点就去看这个位置能不能去大西洋和太平洋。
- 如果可以就加入到最终结果。然后在去下一个位置。
解法一:直接判断某一个位置能否去大西洋和太平洋
- 但是这种直接判断的方式会存在非常多的重复路径,一旦矩阵规模很大这样搞可能会超时。
- 因此我们不能直接解决这个问题。
拿到一个点就暴力去判断能不能去大西洋和太平洋。我们要换一种方法来解决这个问题。
解法二:正难则反
我们要找的是某个位置能不能流向太平洋和大西洋,那我能不能反过来过看大西洋和太平洋能否流到相同的位置!
- 比如说先看太平洋 上左两条边界开始 水能流向那些位置。
- 如果我反向能从太平洋流向某个位置,那这个位置正向也一定能从这个位置流向太平洋。
- 正向是从某个位置流向小于或者等于我的位置。
那反向就是流向大于或者等于我的位置。
- 比如从1开始,一次就可以找到有这么多位置可以从1流向太平洋。
- 然后在上面这一行下一个位置,但是因为1已经把这些位置标记了,所以不会在进去了。
- 这一行都直接结束了。
- 然后考虑左边这一列,没有被标记的地方我可以去。
- 被标记的地方我都不用去了。
- 因为遍历过的地方只会遍历一次,所以时间复杂度会降的非常多。
我们反着从最上面一行最左边一列找的这些点都可以流向太平洋。
借助标记,来优化爆搜。从边界开始,利用DFS找到并 标记 能到达的位置
(爆搜四条边所能到达的位置,对搜到的点进行标记)
- 同理从最下面一行和最右边一列找大西洋能流向那些位置。
- 然后就会发现有一些重复标记的位置。
而这些重复标记的位置就是既能流向太平洋又能流向大西洋。
class Solution {
public:
int dx[4]={0,0,1,-1};
int dy[4]={1,-1,0,0};
int m,n;
vector<vector<bool>> pvis,avis;
vector<vector<int>> ret;
//双标记法 不要搅合在一起
//!!!!!!清晰 正确是首要! 不要偷懒
vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights)
{
m=heights.size();
n=heights[0].size();
pvis.resize(m,vector<bool>(n,false));
avis.resize(m,vector<bool>(n,false));
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(i==0 || j==0)
{
pvis[i][j]=true;
dfs(heights,i,j,pvis);
}
if(i==m-1 || j==n-1)
{
avis[i][j]=true;
dfs(heights,i,j,avis);
}
}
}
for(int i=0;i<m;i++)
{
for(int j=0;j<n;j++)
{
if(pvis[i][j]==true && avis[i][j]==true)
ret.push_back({i,j});
}
}
return ret;
}
//如何在DFS中区分双标记位呢
//!!!!!!! 把两种VIS的处理方式, 当作DFS的参数来传递
void dfs(vector<vector<int>>& heights,int i,int j,vector<vector<bool>>& vis)
{
for(int k=0;k<4;k++)
{
int x=i+dx[k],y=j+dy[k];
if(x>=0 && x<m && y>=0 && y<n
&& heights[i][j]<=heights[x][y]
&& !vis[x][y])
{
vis[x][y]=true;
dfs(heights,x,y,vis);
}
}
}
};
双标记位
双标记法 不要搅合在一起
- !!!!!!清晰 正确是首要! 不要偷懒
如何在DFS中区分双标记位呢
- !!!!!!! 把两种VIS的处理方式, 当作DFS的参数来传递