417. 太平洋大西洋水流问题(DFS)

本文介绍了一种算法,用于解决水流既能流向太平洋又能流向大西洋的问题。通过两次遍历,分别找出可以流向两个不同方向海洋的单元格,并最终确定同时满足两个条件的单元格。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 题目

给定一个 m x n 的非负整数矩阵来表示一片大陆上各个单元格的高度。“太平洋”处于大陆的左边界和上边界,而“大西洋”处于大陆的右边界和下边界。
规定水流只能按照上、下、左、右四个方向流动,且只能从高到低或者在同等高度上流动。
请找出那些水流既可以流动到“太平洋”,又能流动到“大西洋”的陆地单元的坐标。
提示:

  • 输出坐标的顺序不重要
  • m 和 n 都小于150

在这里插入图片描述

2. 解题思路

这题和130题有点类似,都是从边界往内寻找。
这题呢我们从边界出发,假设先从上边出发,也就是太平洋。 靠近上边的水流一定都可以流入太平洋,那么我们只需要将“上边”遍历,找到高度比它高的,就可以通过它流入太平洋。其它三条边也是这样。只不过右边和下边是可以流入大西洋。

解释一下:题目说高度高的可以向高度低的或者高度相等的流动。 也就是说,只要比上边的水流的高度高,或者相等,那么该水流就可以流到上边,而上边又和太平洋相连,即该水流可流入太平洋。

这样,两轮循环,一轮用来找到可以流入太平洋的水流, 一轮找到可以流入大西洋的水流,把它们分别放入两个boolean数组中,然后再来一次整个数组遍历,如果有一个水流 即可流入太平洋也可流入大西洋(toPa[i][j] && toAt[i][j])。那么它就是我们要寻找的。把它的坐标放入res,返回即可。

[!NOTE] 具体操作
我们不是从每个格子出发去判断是否能流到两个海洋(这样每个格子都要进行 DFS,效率低),而是反过来

从太平洋和大西洋的边界出发,向内做 DFS(或 BFS),标记哪些格子能“被水流到”。

具体做法如下:

  1. 建立两个布尔矩阵 toPa[][]toAt[][]
    • toPa[i][j] = true 表示从格子 (i,j) 可以逆流到太平洋边界
    • toAt[i][j] = true 表示从格子 (i,j) 可以逆流到大西洋边界
  2. 分别从四条边界出发
    • 太平洋边界:第一行 + 第一列
    • 大西洋边界:最后一行 + 最后一列
    • 对每个边界格子进行 DFS,遵循规则:只有下一个格子的高度 ≥ 当前格子时,才允许逆流过去
  3. 最后遍历整个矩阵
    • 如果某个格子同时被两个布尔矩阵标记,即可流向两个海洋,加入结果

本题的重点就是 中间的水流可以通过四条边的水流 流入对应的海洋。

[!question] 两个for循环最开始height为什么是Integer最小值?
因为四条边肯定可以流入对应的海洋,所以设置为最小值避免大于四条边的高度

3. 代码

class Solution {
    public List<List<Integer>> pacificAtlantic(int[][] matrix) {
        List<List<Integer>> res = new ArrayList<>();

        if (matrix.length == 0 || matrix[0].length == 0) {
            return res;
        }

        int r = matrix.length; // 行数,可以看成 y 轴
        int c = matrix[0].length; // 列数,可以看成 x 轴
        boolean[][] toPa = new boolean[r][c];
        boolean[][] toAt = new boolean[r][c];

        // 太平洋边界:左边界 & 上边界
        for (int i = 0; i < r; i++) {
            DFS(i, 0, matrix, toPa, Integer.MIN_VALUE);       // 左边界
            DFS(i, c - 1, matrix, toAt, Integer.MIN_VALUE);   // 右边界(大西洋)
        }

        for (int i = 0; i < c; i++) {
            DFS(0, i, matrix, toPa, Integer.MIN_VALUE);       // 上边界
            DFS(r - 1, i, matrix, toAt, Integer.MIN_VALUE);   // 下边界(大西洋)
        }

        // 遍历整个二维数组,找出能同时到达两个海洋的点
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                if (toPa[i][j] && toAt[i][j]) {
                    List<Integer> cur = new ArrayList<>();
                    cur.add(i);
                    cur.add(j);
                    res.add(cur);
                }
            }
        }

        return res;
    }

    private void DFS(int r, int c, int[][] matrix, boolean[][] toSea, int height) {
        if (r < 0 || r >= matrix.length
                || c < 0 || c >= matrix[0].length
                || toSea[r][c]
                || matrix[r][c] < height) {
            return;
        }

        toSea[r][c] = true;

        // 枚举四个方向
        DFS(r + 1, c, matrix, toSea, matrix[r][c]);
        DFS(r - 1, c, matrix, toSea, matrix[r][c]);
        DFS(r, c + 1, matrix, toSea, matrix[r][c]);
        DFS(r, c - 1, matrix, toSea, matrix[r][c]);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值