一.题目
417. 太平洋大西洋水流问题 - 力扣(LeetCode)

二.思路讲解
2.1 审题
本题要求找出矩阵中所有能够同时流向太平洋和大西洋 的格子。水可以从一个格子流向上下左右 相邻的格子,条件是相邻格子的高度 ≤ 当前格子的高度 。太平洋位于矩阵的上边界和左边界 ,大西洋位于下边界和右边界。
2.2 思路讲解
直接判断每个格子是否能同时流向两个海洋比较困难,但可以采用正难则反 的思路:从海洋出发,反向标记能流到海洋的格子。具体做法:
-
从太平洋边界(上、左)出发,反向进行深度优先搜索(DFS),标记所有能流到太平洋的格子。反向搜索时,水可以从低处流向高处,即相邻格子高度 ≥ 当前格子高度时才能流动。
-
从大西洋边界(下、右)出发,同样反向搜索,标记所有能流到大西洋的格子。
-
最后,同时被两个海洋标记的格子就是既能流向太平洋又能流向大西洋的格子。
三.代码演示
cpp
class Solution
{
public:
int row,col;
int bx[4] = {0,0,1,-1};
int by[4] = {1,-1,0,0};
vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights)
{
row = heights.size();col = heights[0].size();
vector<vector<bool>>Pac(row,vector<bool>(col));
vector<vector<bool>>Atl(row,vector<bool>(col));
//处理太平洋
for(int j = 0;j < col;j++) dfs(heights,0,j,Pac);
for(int i = 0;i < row;i++) dfs(heights,i,0,Pac);
//处理大西洋
for(int i = 0;i < row;i++) dfs(heights,i,col - 1,Atl);
for(int j = 0;j < col;j++) dfs(heights,row - 1,j,Atl);
//处理二者相交部分
vector<vector<int>> ret;
for(int i = 0;i < row;i++)
{
for(int j = 0;j < col;j++)
{
if(Pac[i][j] && Atl[i][j])
{
ret.push_back({i,j});
}
}
}
return ret;
}
void dfs(vector<vector<int>>& heights,int i,int j,vector<vector<bool>>& cur)
{
cur[i][j] = true;
for(int k = 0;k < 4;k++)
{
int x = i + bx[k],y = j + by[k];
if(x >= 0 && y >= 0 && x < row && y < col && !cur[x][y] && heights[i][j] <= heights[x][y])
{
dfs(heights,x,y,cur);
}
}
}
};
四.代码讲解
一、全局变量与方向数组
-
row、col:成员变量,存储网格的行数和列数。 -
方向数组
bx[4]、by[4]:表示上下左右四个方向的偏移量,用于探索相邻格子。
二、主函数 pacificAtlantic
-
获取网格尺寸 :
row = heights.size(); col = heights[0].size(); -
初始化两个布尔矩阵:
-
Pac:记录能流到太平洋的格子,初始全false。 -
Atl:记录能流到大西洋的格子,初始全false。
-
-
从太平洋边界出发进行反向DFS:
-
遍历第一行(
i = 0)的所有列,调用dfs(heights, 0, j, Pac)。 -
遍历第一列(
j = 0)的所有行,调用dfs(heights, i, 0, Pac)。 -
注意:左上角格子
(0,0)会被调用两次,但dfs内会检查cur[i][j],已访问则直接返回,不会重复。
-
-
从大西洋边界出发进行反向DFS:
-
遍历最后一行(
i = row-1)的所有列,调用dfs(heights, i, col-1, Atl)。 -
遍历最后一列(
j = col-1)的所有行,调用dfs(heights, row-1, j, Atl)。
-
-
收集既能流向太平洋又能流向大西洋的格子:
-
遍历整个网格,如果
Pac[i][j]和Atl[i][j]都为true,则将坐标{i, j}加入结果集。 -
返回结果集。
-
三、递归函数 dfs
dfs(heights, i, j, cur) 从格子 (i, j) 开始,反向标记所有能流到当前海洋的格子。执行流程:
-
标记当前格子 :
cur[i][j] = true,表示该格子能流向对应的海洋。 -
向四个方向探索 :遍历四个方向,计算新坐标
(x, y)。-
边界检查 :
x >= 0 && y >= 0 && x < row && y < col -
未访问 :
!cur[x][y] -
反向流动条件 :
heights[i][j] <= heights[x][y]-
正向水从高处流向低处(
h_current >= h_neighbor)。 -
反向搜索时,如果邻居的高度 大于等于 当前格子,则水可以从邻居流向当前格子,因此当前格子能流向海洋 → 邻居也能流向海洋。
-
-
-
若满足条件,则递归调用
dfs(heights, x, y, cur),继续标记。
四、关键细节
-
反向搜索的条件 :
heights[i][j] <= heights[x][y]确保从低处向高处搜索,这与正向水流方向相反,但正是标记能流到海洋的格子的正确方式。 -
标记数组重用 :
Pac和Atl既是访问标记,也是最终结果标记,节省空间。 -
避免重复访问 :在递归入口先检查
cur[i][j],但代码中是在递归调用时用!cur[x][y]判断,而当前格子已在进入时标记,因此不会重复。
五、流程图
