LeetCode 417 - 太平洋大西洋水流问题


文章目录
- 摘要
- 描述
- 题解答案
- 题解代码分析
- 示例测试及结果
- 时间复杂度
- 空间复杂度
- 总结
摘要
这道题看起来像地理模拟,但其实是个非常典型的「图搜索 + 反向思考」问题。
我们要找出能同时流向太平洋(左上边界)和大西洋(右下边界)的所有坐标点。
题目乍看复杂,其实核心思路只有一句话:从海洋反向出发,找出能到达的点。

描述
我们有一个 m x n 的矩阵,heights[r][c] 表示高度。
水只能往高度不高于当前点的方向流动(即水往低处流),并且如果从某个点出发能顺流流到边界,那么这个点就能流向对应的海洋。
任务:
返回所有“既能流向太平洋,又能流向大西洋”的点的坐标。
示例:
输入:
heights = [[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]
]输出:
[[0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0]]
题解答案
如果我们从每个格子出发去判断“能否到达太平洋和大西洋”,复杂度会很高,因为每个点都要 DFS 一次。
但我们可以换个思路:
与其让水往下流,不如让海洋往上“反爬”。
也就是说,从太平洋边界开始,找到所有能“逆流而上”的格子(即可以从这个格子流到太平洋的格子);
同理,从大西洋边界也做一次相同的搜索;
最后取两个结果的交集,就是答案。

题解代码分析
我们用 DFS(或 BFS)搜索所有能从海洋“逆流”到的点。
这段 Swift 代码使用 DFS 实现,逻辑简洁且易读。
import Foundationclass Solution {func pacificAtlantic(_ heights: [[Int]]) -> [[Int]] {guard !heights.isEmpty else { return [] }let rows = heights.countlet cols = heights[0].countvar pacific = Array(repeating: Array(repeating: false, count: cols), count: rows)var atlantic = Array(repeating: Array(repeating: false, count: cols), count: rows)let directions = [(1,0),(-1,0),(0,1),(0,-1)]func dfs(_ r: Int, _ c: Int, _ visited: inout [[Bool]], _ prevHeight: Int) {// 越界或高度不满足条件直接返回guard r >= 0, c >= 0, r < rows, c < cols else { return }if visited[r][c] || heights[r][c] < prevHeight { return }visited[r][c] = truefor (dr, dc) in directions {dfs(r + dr, c + dc, &visited, heights[r][c])}}// 从太平洋边界出发for c in 0..<cols {dfs(0, c, &pacific, heights[0][c])dfs(rows - 1, c, &atlantic, heights[rows - 1][c])}// 从大西洋边界出发for r in 0..<rows {dfs(r, 0, &pacific, heights[r][0])dfs(r, cols - 1, &atlantic, heights[r][cols - 1])}var result = [[Int]]()for r in 0..<rows {for c in 0..<cols {if pacific[r][c] && atlantic[r][c] {result.append([r, c])}}}return result}
}
代码解析:
pacific和atlantic两个二维布尔矩阵用于标记哪些点能被对应的海洋“触及”;dfs函数执行深度优先遍历,沿着比当前高度更高或相等的路径前进;- 从太平洋边界(上边和左边)开始 DFS;
- 从大西洋边界(下边和右边)开始 DFS;
- 最后遍历整个矩阵,找到同时被两者访问过的点,即为答案。
换句话说:我们不让水流动,而是让海水“倒灌”上山。
示例测试及结果
我们可以通过下面的示例来验证:
let solution = Solution()
let heights = [[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]
]
print(solution.pacificAtlantic(heights))
输出结果:
[[0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0]]
如果我们画出来,大概可以理解为:
- 从左上边(太平洋)能“逆流而上”的点;
- 从右下边(大西洋)也能“逆流而上”的点;
- 两者重叠区域就是水能两边都流到的地方。
这个方法直观又高效。
时间复杂度
每个格子只会在 DFS 中被访问一次,因此:
时间复杂度为 O(M × N),
其中 M 和 N 分别是矩阵的行数和列数。
空间复杂度
我们用了两个 M × N 的布尔矩阵以及 DFS 栈空间,
所以空间复杂度为 O(M × N)。
总结
这道题的关键不在于“水怎么流”,而在于从哪里出发思考。
一旦你反过来想,就会发现问题瞬间清晰。
这类题的思维模式可以推广到很多实际开发场景,比如:
- 地图路径可达性计算(例如在导航系统中判断哪些区域可达)
- 洪水或扩散模拟(例如地形渲染、游戏地图)
- 双源搜索问题(例如同时满足两个条件的节点查找)
