在问 · 算法题解

417. 太平洋大西洋水流问题

中等图论逆流广度优先搜索力扣原题 ↗

题目描述

一座岛屿被划成 m x n 的方格,heights[r][c] 是格子 (r, c) 的海拔。岛的上边缘和左边缘挨着太平洋,下边缘和右边缘挨着大西洋。

雨水落在某格后,可以流向上、下、左、右四个相邻格中海拔不高于当前格的那一格,并可以连续流动。位于边缘的格子可以把水直接排进它所挨着的海洋。

找出所有这样的格子:从它出发的雨水既能到达太平洋,也能到达大西洋。

返回这些格子的坐标列表,每个坐标写成 [r, c],行列下标都从 0 开始。坐标的先后顺序不限。

示例 1
输入: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]]
示例 2
输入:heights = [[2,1],[1,2]]
输出:[[0,0],[0,1],[1,0],[1,1]]

解释:(0,1) 和 (1,0) 同时挨着两片海洋;(0,0) 和 (1,1) 可以流向它们,因此四格全部入选。

数据范围

  • m == heights.length
  • n == heights[r].length
  • 1 <= m, n <= 200
  • 0 <= heights[r][c] <= 10^5

解题思路:逆流广度优先搜索

顺着水流从每个格子出发搜索太慢。反过来想:从海边“逆流而上”,只能走向高度 ≥ 当前的邻格。从太平洋边界做一次多源 BFS,得到能流到太平洋的格子集合 pac;从大西洋边界再做一次得到 atl。两个集合的交集就是答案。

为什么正确

被某次 BFS 标记的格子,都存在一条高度单调不增的路径流向对应的海洋。

复杂度分析

每次 BFS 每格最多入队一次,时间 O(mn),空间 O(mn)。

易错点

逆流时比较方向容易写反:应该是 heights[x][y] >= heights[i][j](邻格不低于当前);另外相等高度也能流动。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]:
        m, n = len(heights), len(heights[0])

        def bfs(starts):
            seen = set(starts)
            queue = list(starts)
            head = 0
            while head < len(queue):
                i, j = queue[head]
                head += 1
                for x, y in ((i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1)):
                    if 0 <= x < m and 0 <= y < n and (x, y) not in seen and heights[x][y] >= heights[i][j]:
                        seen.add((x, y))
                        queue.append((x, y))
            return seen

        pac = bfs([(i, 0) for i in range(m)] + [(0, j) for j in range(1, n)])
        atl = bfs([(i, n - 1) for i in range(m)] + [(m - 1, j) for j in range(n - 1)])
        return [[i, j] for i in range(m) for j in range(n) if (i, j) in pac and (i, j) in atl]

载入编辑器,自己跑一遍 →

其他解法

两种方法都从海洋边界逆流搜索,复杂度相同;BFS 用队列,没有栈溢出风险,DFS 写法更简洁。

解法 2:逆流深度优先搜索 时间 O(m·n),空间 O(m·n)

从太平洋边界的每个格子出发做 DFS,只走向高度不低于当前格的邻格,标记能到达太平洋的格子。对大西洋边界做同样的 DFS。两组标记都为真的格子就是答案。

代码比 BFS 更短。递归深度最坏为 m·n,网格很大时可能栈溢出,需要改用显式栈。

class Solution:
    def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]:
        m, n = len(heights), len(heights[0])

        def dfs(seen, i, j):
            # 网格可达 4 万格,Python 用显式栈模拟递归,访问顺序与递归一致
            seen[i][j] = True
            stack = [(i, j)]
            while stack:
                i, j = stack.pop()
                for x, y in ((i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1)):
                    if 0 <= x < m and 0 <= y < n and not seen[x][y] and heights[x][y] >= heights[i][j]:
                        seen[x][y] = True
                        stack.append((x, y))

        pac = [[False] * n for _ in range(m)]
        atl = [[False] * n for _ in range(m)]
        for i in range(m):
            if not pac[i][0]:
                dfs(pac, i, 0)
            if not atl[i][n - 1]:
                dfs(atl, i, n - 1)
        for j in range(n):
            if not pac[0][j]:
                dfs(pac, 0, j)
            if not atl[m - 1][j]:
                dfs(atl, m - 1, j)
        return [[i, j] for i in range(m) for j in range(n) if pac[i][j] and atl[i][j]]

图论 专题的其他题目

查看 图论 全部题目 →