leetcode417. 太平洋大西洋水流问题
写出来的很多bug。直接看题解。
算法思路解释:
-
逆向思维:不是从每个单元格出发寻找是否能到达海洋,而是从海洋边界出发,逆向寻找可以流向海洋的单元格。
-
两个标记矩阵:
-
toPacific
:记录能流向太平洋的单元格 -
toAtlantic
:记录能流向大西洋的单元格
-
-
DFS搜索过程:
-
从太平洋边界(矩阵左边界和上边界)开始DFS,标记所有能流向太平洋的单元格
-
从大西洋边界(矩阵右边界和下边界)开始DFS,标记所有能流向大西洋的单元格
-
水只能从高往低流或平流,所以搜索条件是
heights[newX][newY] >= heights[x][y]
-
-
结果收集:
-
遍历所有单元格,找出同时被两个矩阵标记的单元格
-
这些单元格就是能同时流向太平洋和大西洋的单元格
-
关键点说明:
-
方向数组:使用
dX
和dY
数组可以方便地处理四个方向的移动 -
边界处理:DFS从边界开始,因为边界单元格直接与海洋相连
-
避免重复:在遍历边界时跳过角落单元格,避免重复处理
-
逆向思维:这种方法比从每个单元格出发正向搜索更高效,时间复杂度为O(mn)
class Solution {
public:
int m, n; // m是矩阵行数,n是矩阵列数
int dX[4] = {0, 0, 1, -1}; // 四个方向的x坐标变化:右、左、下、上
int dY[4] = {1, -1, 0, 0}; // 四个方向的y坐标变化:右、左、下、上
// 深度优先搜索函数,标记可以流向某个海洋的单元格
void dfs(int x, int y, vector<vector<int>>& heights, vector<vector<bool>>& ocean) {
// 如果当前单元格已经被标记过,直接返回
if (ocean[x][y]) return;
// 标记当前单元格为可流向该海洋
ocean[x][y] = true;
// 探索四个方向
for (int i = 0; i < 4; ++i) {
int newX = x + dX[i]; // 计算新的行坐标
int newY = y + dY[i]; // 计算新的列坐标
// 检查新坐标是否在矩阵范围内
if (newX >= 0 && newX < m && newY >= 0 && newY < n) {
// 只有当新单元格高度≥当前单元格高度时才继续搜索
// (因为水只能从高往低流或平流)
if (heights[newX][newY] >= heights[x][y]) {
dfs(newX, newY, heights, ocean);
}
}
}
}
vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights) {
m = heights.size(); // 获取行数
n = heights[0].size(); // 获取列数
// 创建两个矩阵来记录能流向太平洋和大西洋的单元格
vector<vector<bool>> toPacific(m, vector<bool>(n, false)); // 太平洋可达性
vector<vector<bool>> toAtlantic(m, vector<bool>(n, false)); // 大西洋可达性
// 从太平洋边界(左边界和上边界)开始DFS
for (int i = 0; i < m; ++i) dfs(i, 0, heights, toPacific); // 左边界
for (int j = 1; j < n; ++j) dfs(0, j, heights, toPacific); // 上边界(跳过(0,0)避免重复)
// 从大西洋边界(右边界和下边界)开始DFS
for (int i = 0; i < m; ++i) dfs(i, n - 1, heights, toAtlantic); // 右边界
for (int j = 0; j < n - 1; ++j) dfs(m - 1, j, heights, toAtlantic); // 下边界(跳过(m-1,n-1)避免重复)
// 收集能同时流向两个海洋的单元格
vector<vector<int>> res;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (toAtlantic[i][j] && toPacific[i][j]) {
res.push_back({i, j}); // 如果能同时流向两个海洋,加入结果
}
}
}
return res;
}
};
deepseek v3写的BFS版本:
class Solution {
public:
int m, n; // 矩阵的行数和列数
int dX[4] = {0, 0, 1, -1}; // 四个方向的x坐标变化:右、左、下、上
int dY[4] = {1, -1, 0, 0}; // 四个方向的y坐标变化:右、左、下、上
// BFS函数,用于标记可以流向某个海洋的单元格
void bfs(queue<pair<int, int>>& q, vector<vector<int>>& heights, vector<vector<bool>>& ocean) {
while (!q.empty()) {
auto [x, y] = q.front();
q.pop();
// 如果当前单元格已经被标记过,跳过
if (ocean[x][y]) continue;
// 标记当前单元格为可流向该海洋
ocean[x][y] = true;
// 探索四个方向
for (int i = 0; i < 4; ++i) {
int newX = x + dX[i];
int newY = y + dY[i];
// 检查新坐标是否在矩阵范围内
if (newX >= 0 && newX < m && newY >= 0 && newY < n) {
// 只有当新单元格高度≥当前单元格高度时才加入队列
// (因为水只能从高往低流或平流)
if (heights[newX][newY] >= heights[x][y] && !ocean[newX][newY]) {
q.push({newX, newY});
}
}
}
}
}
vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights) {
m = heights.size(); // 获取行数
n = heights[0].size(); // 获取列数
// 创建两个矩阵来记录能流向太平洋和大西洋的单元格
vector<vector<bool>> toPacific(m, vector<bool>(n, false));
vector<vector<bool>> toAtlantic(m, vector<bool>(n, false));
// 创建两个队列用于BFS
queue<pair<int, int>> pacificQueue;
queue<pair<int, int>> atlanticQueue;
// 初始化太平洋队列(左边界和上边界)
for (int i = 0; i < m; ++i) pacificQueue.push({i, 0});
for (int j = 1; j < n; ++j) pacificQueue.push({0, j});
// 初始化大西洋队列(右边界和下边界)
for (int i = 0; i < m; ++i) atlanticQueue.push({i, n - 1});
for (int j = 0; j < n - 1; ++j) atlanticQueue.push({m - 1, j});
// 执行BFS搜索
bfs(pacificQueue, heights, toPacific);
bfs(atlanticQueue, heights, toAtlantic);
// 收集能同时流向两个海洋的单元格
vector<vector<int>> res;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (toAtlantic[i][j] && toPacific[i][j]) {
res.push_back({i, j});
}
}
}
return res;
}
};