leetcode
leetcode 351 ~ 400
太平洋大西洋水流问题

太平洋大西洋水流问题

难度:

标签:

题目描述

有一个 m × n 的矩形岛屿,与 太平洋大西洋 相邻。 “太平洋” 处于大陆的左边界和上边界,而 “大西洋” 处于大陆的右边界和下边界。

这个岛被分割成一个由若干方形单元格组成的网格。给定一个 m x n 的整数矩阵 heights , heights[r][c] 表示坐标 (r, c) 上单元格 高于海平面的高度

岛上雨水较多,如果相邻单元格的高度 小于或等于 当前单元格的高度,雨水可以直接向北、南、东、西流向相邻单元格。水可以从海洋附近的任何单元格流入海洋。

返回网格坐标 result 的 2D 列表 ,其中 result[i] = [ri, ci] 表示雨水从单元格 (ri, ci) 流动 既可流向太平洋也可流向大西洋

 

示例 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]]

 

提示:

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

代码结果

运行时间: 73 ms, 内存: 17.5 MB


/*
 * Solution in Java using Streams
 * This implementation is similar to the regular Java solution, but makes use of Java Stream API for readability.
 * The core logic is still based on DFS to mark reachable cells.
 */
 
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
 
public class Solution {
    public List<int[]> pacificAtlantic(int[][] heights) {
        int m = heights.length, n = heights[0].length;
        boolean[][] pacific = new boolean[m][n];
        boolean[][] atlantic = new boolean[m][n];
 
        IntStream.range(0, m).forEach(i -> {
            dfs(heights, pacific, i, 0);
            dfs(heights, atlantic, i, n - 1);
        });
        IntStream.range(0, n).forEach(j -> {
            dfs(heights, pacific, 0, j);
            dfs(heights, atlantic, m - 1, j);
        });
 
        return IntStream.range(0, m)
            .boxed()
            .flatMap(i -> IntStream.range(0, n)
                .filter(j -> pacific[i][j] && atlantic[i][j])
                .mapToObj(j -> new int[]{i, j}))
            .collect(Collectors.toList());
    }
 
    private void dfs(int[][] heights, boolean[][] ocean, int i, int j) {
        int m = heights.length, n = heights[0].length;
        ocean[i][j] = true;
        int[][] directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
        for (int[] dir : directions) {
            int x = i + dir[0], y = j + dir[1];
            if (x >= 0 && x < m && y >= 0 && y < n && !ocean[x][y] && heights[x][y] >= heights[i][j]) {
                dfs(heights, ocean, x, y);
            }
        }
    }
}

解释

方法:

该题解采用逆向思维的方式。水从高处往低处流,那么可以从太平洋和大西洋的沿岸点开始向内搜寻,找到比自己高的点,逆流而上。对于每个海洋,分别从行和列的边界开始深度优先搜索,标记能够到达该海洋的点。最后找到既能流到太平洋又能流到大西洋的点,即为满足要求的结果。

时间复杂度:

O(mn)

空间复杂度:

O(mn)

代码细节讲解

🦆
为什么在深度优先搜索(DFS)中,选择从海洋边缘的单元格开始搜索而不是从内陆开始?
在太平洋大西洋水流问题中,采用逆向思维策略是因为直接模拟水从高处向低处流动的过程在实现上较为复杂,且难以直接确定流向两个大洋的起点。从海洋边缘单元格开始向内部逆流搜索可以简化问题,因为边缘单元格自然与海洋相连,只需要检查能够逆流到达边缘单元格的内陆单元格。这样可以有效地标记出所有可以流入特定大洋的单元格,从而简化问题解决过程。
🦆
在深度优先搜索中,标记过程中为何只考虑与当前单元格高度相等或更高的相邻单元格?
在逆向思维的策略中,从海洋边缘向内部进行搜索时,我们模拟的是水从低处向高处的逆流过程。因此,只有当一个相邻单元格的高度大于或等于当前单元格的高度时,水才可能从该相邻单元格流向当前单元格。这种策略确保了我们正确地模拟了水流的逆流路径,避免了错误地将水流向实际上无法到达的更低的区域。
🦆
当在DFS中检查一个单元格是否已访问时,如果已访问单元格的高度小于当前单元格,是否应重新访问以模拟更多的水流路径?
在本题策略中,即使已访问的单元格高度小于当前单元格,也无需重新访问。由于我们从海洋边缘向内部逆流搜索,标记过程中已经保证了只有那些能够通过逆流到达边缘单元格的路径被标记。因此,如果一个单元格在之前已被标记为可访问,即意味着已确定它可以流向对应的大洋,无论从哪条路径到达。重复访问已标记的更低单元格并不会提供新的信息,因为水不会从低处自动流向更高处。
🦆
在题解中提到的递归深度是`min(m, n)`,这是怎么得出的?在最坏情况下递归深度是否可能更大?
题解中提到的递归深度`min(m, n)`可能是基于最优或平均情况的考虑,假设水流从一边缘逐步向另一边缘流动,且每次都能向内延伸一行或一列。然而,实际上在最坏情况下,递归深度可以更大,尤其是当矩阵中的高度配置允许水流在复杂路径中循环移动时。理论上,递归深度可以达到`m * n`,尤其是在一个高度差异极小且布局复杂的矩阵中。

相关问题