代码随想录——接雨水(Leetcode hot7)需要回顾

题目链接
在这里插入图片描述

我的解法(和答案比很麻烦的双指针)

思路:从两边向中间进行两次扫描,分别处理左边和右边的蓄水情况

  1. 第一轮扫描(从左向右):
  • 使用两个指针 slow 和 fast,slow 用来记录当前的最高柱子的位置,fast 用来探索后续柱子的高度。
  • 如果 fast 位置的高度 height[fast] 小于 slow 位置的高度 height[slow],那么 fast 继续向右移动。
  • 一旦 fast 找到了一个高度比 slow 高或相等的柱子,说明可以在 slow 和 fast 之间蓄水。此时,遍历 slow 和 fast 之间的每一个位置,计算能够蓄水的量,蓄水量是 Math.min(height[fast], height[slow]) - height[i],其中 i 是 slow 和 fast 之间的位置。
  • 然后,将 slow 更新为 fast,并让 fast 移动到 slow 的下一个位置,继续进行下一轮的探索。
  1. 第二轮扫描(从右向左):
  • 如果第一轮扫描结束后,slow 没有移动到数组的最右端,意味着可能还存在从右向左方向可以蓄水的槽,因此需要进行第二轮扫描。
  • 第二轮扫描与第一轮类似,只是从数组的右端开始,fast 指向数组的最右端,slow 指向 fast 左侧的柱子。
  • 如果 slow 位置的高度 height[slow] 小于或等于 fast 位置的高度 height[fast],slow 左移,继续寻找能够蓄水的槽。
  • 一旦找到了一个 slow 位置的高度大于 fast 的柱子,开始在 slow 和 fast 之间计算蓄水量,并更新 fast 和 slow 位置,继续进行下一轮的探索。
  1. 终止条件:
    当 fast 和 slow 在第一轮或第二轮扫描中分别移动到数组的两端时,算法结束,返回最终的蓄水量 res。

  2. 关键思路总结:

  • 双向扫描:代码通过两次扫描(从左向右、从右向左)来确保不会遗漏任何可能蓄水的槽。
  • 局部最低点:每一轮扫描都会寻找局部的最低点,计算蓄水量。
  • 重置指针:在每次找到一个新的可以形成蓄水槽的 fast 位置后,slow 和 fast 指针会重新定位,以继续寻找下一个蓄水槽。
class Solution {
    public int trap(int[] height) {
        int res = 0;
        int slow = 0;
        int fast = slow + 1;
        while(fast < height.length){
            if(height[fast] < height[slow]){
                fast++;
            }else{
                for(int i = slow + 1; i < fast; i++){
                    res += Math.min(height[fast], height[slow]) - height[i];
                }
                slow = fast;
                fast = slow + 1;
            }
        }
         // 如果最后slow没有更新到右端,还需要从右向左再扫描一次
        fast = height.length - 1;
        slow = fast - 1;
        while(slow >= 0){
            if(height[slow] <= height[fast]){
                slow--;
            }else{
                for(int i = slow + 1; i < fast; i++){
                    res += Math.min(height[fast], height[slow]) - height[i];
                }
                fast = slow;
                slow = fast - 1;
            }
        }
        return res;
    }
}

题解一(双指针)

总体思路: 双指针法通过一次遍历数组,动态计算左右两侧的最高柱子高度,从而高效地计算出能够接住的雨水总量。

解题思路:

  1. 初始化双指针和最大值:
    left 指针从数组的左端开始,初始值为 0。
    right 指针从数组的右端开始,初始值为 height.length - 1。
    leftMax 用来记录从左到右遍历过程中遇到的最高柱子,初始值为 0。
    rightMax 用来记录从右到左遍历过程中遇到的最高柱子,初始值为 0。
    ans 用来存储最后的结果,也就是能够接住的雨水的总量。
  2. 双指针移动和计算:
    在每一步中,比较 height[left] 和 height[right] 的值:
  • 如果 height[left] < height[right],说明左侧的柱子较低,这意味着可能在左侧积水。我们接下来看 left 指针的高度与 leftMax 的比较:
    • 更新 leftMax 为 leftMax 和 height[left] 中的较大值。这是为了确保 leftMax 始终是从左端到当前 left 位置的最高柱子高度。
    • 计算当前 left 位置能积水的量,即 leftMax - height[left]。如果 leftMax 比 height[left] 高,那么在 left 位置可以积水(两者的差值即为水的高度),将这个值加到 ans 中。
    • left 指针右移,处理下一个位置。
  • 如果 height[right] <= height[left],则说明右侧的柱子较低或相等,我们将使用类似的逻辑来处理 right 指针:
    • 更新 rightMax 为 rightMax 和 height[right] 中的较大值,确保 rightMax 始终是从右端到当前 right 位置的最高柱子高度。
    • 计算当前 right 位置能积水的量,即 rightMax - height[right]。同样,如果 rightMax 比 height[right] 高,积水量就为两者的差值,将其加到 ans 中。
    • 将 right 指针左移,处理下一个位置。

核心思想总结:

  • 动态维护左右两侧的最大高度:通过不断更新 leftMax 和 rightMax,确保始终有当前左右两侧最高的柱子高度,进而可以计算当前位置的积水量
  • 双指针法的优势:通过比较左右两侧的柱子高度,选择较低的一侧进行处理,确保能够正确地计算积水,且无需额外的空间来存储每个位置的最大高度。
class Solution {
    public int trap(int[] height) {
        int ans = 0;
        int left = 0, right = height.length - 1;
        int leftMax = 0, rightMax = 0;
        while (left < right) {
            leftMax = Math.max(leftMax, height[left]);
            rightMax = Math.max(rightMax, height[right]);
            if (height[left] < height[right]) {
                ans += leftMax - height[left];
                ++left;
            } else {
                ans += rightMax - height[right];
                --right;
            }
        }
        return ans;
    }
}

题解二(动态规划)

思路:
如果已经知道每个位置两边的最大高度,则可以在 O(n) 的时间内得到能接的雨水总量。使用动态规划的方法,可以在 O(n) 的时间内预处理得到每个位置两边的最大高度。

创建两个长度为 n 的数组 leftMax 和 rightMax。对于 0≤i<n,leftMax[i] 表示下标 i 及其左边的位置中,height 的最大高度rightMax[i] 表示下标 i 及其右边的位置中,height 的最大高度

显然,leftMax[0]=height[0]rightMax[n−1]=height[n−1]。两个数组的其余元素的计算如下:

  • 1≤i≤n−1 时,leftMax[i]=max(leftMax[i−1],height[i]);

  • 0≤i≤n−2 时,rightMax[i]=max(rightMax[i+1],height[i]);

因此可以正向遍历数组 height 得到数组 leftMax 的每个元素值,反向遍历数组 height 得到数组 rightMax 的每个元素值

在得到数组 leftMax 和 rightMax 的每个元素值之后,对于 0≤i<n,下标 i 处能接的雨水量等于 min(leftMax[i],rightMax[i])−height[i]。遍历每个下标位置即可得到能接的雨水总量。

class Solution {
    public int trap(int[] height) {
        int n = height.length;
        if (n == 0) {
            return 0;
        }

        int[] leftMax = new int[n];
        leftMax[0] = height[0];
        for (int i = 1; i < n; ++i) {
            leftMax[i] = Math.max(leftMax[i - 1], height[i]);
        }

        int[] rightMax = new int[n];
        rightMax[n - 1] = height[n - 1];
        for (int i = n - 2; i >= 0; --i) {
            rightMax[i] = Math.max(rightMax[i + 1], height[i]);
        }

        int ans = 0;
        for (int i = 0; i < n; ++i) {
            ans += Math.min(leftMax[i], rightMax[i]) - height[i];
        }
        return ans;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值