力扣 407. 接雨水 II

题目来源:https://leetcode-cn.com/problems/trapping-rain-water-ii/

大致题意:
给定一个二维数组。把这个数组想象成一个不规则的桶,每个元素的值可以想象成该位置的木板高度。求出这个桶可以装多少容量的水

比如:
3, 3, 3, 3
3, 2, 1, 3
3, 1, 2, 3
3, 4, 4, 4

可以看到中间的
2,1
1,2
就是桶内部可以装水的位置,因为它的边界比这部分都高,那么对应位置可以装水
1,2
2,1
装完之后的高度为
3, 3, 3, 3
3, 3, 3, 3
3, 3, 3, 3
3, 4, 4, 4

思路

既然说到桶装水,那么本题显然和水桶原理有点关系:水桶的装水量由最低的那块木板决定
回到本体,显然边界的位置不能装水,那么它们就像是桶边,那么装水量显然和最低的那个位置有关系。也就是,不管内部的容器有多高或者多低,反正放的水的高度都受现在边界的最低高度限制。
那么显然,现在最低边界相邻的那个内部位置高度与边界高度的关系有:

  • 边界高度大于内部位置。这种情况下,无论该位置其它三个方向的相邻高度为多少,该位置都可以放水,容量为 边界高度 - 内部位置高度
  • 边界高度小于等于内部位置。这种情况下,该位置肯定不能放水

然后,不管该位置放水与否,我们都将其视为新的边界:

  • 若它放水了,那么根据水桶原理,它相邻不会再有比当前位置更高的水面,于是它可以想象成一块和水面一样高的木板,也就是边界
  • 若它没放水,肯定是高于原来的边界,就可以想象成一块钉在外部木板的内部木板,一个新边界

接下来,我们继续刚刚的操作,对下一个最低边界做这样的操作
于是可以使用优先队列存下边界的位置和高度的二元组。并标记放入的位置,防止重复放入

那么,每次都弹出队首元素,即还未处理的最低边界,然后进行上述操作,每次形成新边界时都放入队列,对于放入过队列的元素都进行标记

代码:

public int trapRainWater(int[][] heightMap) {
        int m = heightMap.length;
        int n = heightMap[0].length;
        // 只有桶边,没有内容
        if (m <= 2 || n <= 2) {
            return 0;
        }
        // 用来存储边界 (坐标,高度)
        PriorityQueue<int[]> border = new PriorityQueue<>((o1, o2) -> o1[1] - o2[1]);
        // 用来标记访问过的位置,也就是被认为为边界的位置
        boolean[][] vis = new boolean[m][n];
        // 用来取出上下左右四个方向
        int[] direct = new int[]{-1, 0, 1, 0, -1};
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                // 将边界放入堆,并标记
                if (i == 0 || i == m - 1 || j == 0 || j == n - 1) {
                    // 坐标转换成一维
                    border.add(new int[]{i * n + j, heightMap[i][j]});
                    vis[i][j] = true;
                }
            }
        }
        int ans = 0;
        // 遍历所有格子
        while (!border.isEmpty()) {
            // 取出当前的最低边界
            int[] cur = border.poll();
            // 遍历该边界四个方向中还未访问的格子
            for (int i = 0; i < 4; i++) {
                int nx = cur[0] / n + direct[i];
                int ny = cur[0] % n + direct[i + 1];
                if (nx >= 0 && nx < m && ny >= 0 && ny < n && !vis[nx][ny]) {
                    // 如果边界大于该格子,那么该格子可以放雨水
                    if (cur[1] > heightMap[nx][ny]) {
                        ans += cur[1] - heightMap[nx][ny];
                    }
                    // 将该格子放入堆,更新为新边界
                    // 边界值为 格子高度 与 当前边界 之间的最大值
                    border.add(new int[]{nx * n + ny, Math.max(heightMap[nx][ny], cur[1])});
                    vis[nx][ny] = true;
                }
            }
        }
        return ans;

    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三更鬼

谢谢老板!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值