LeetCode 42.接雨水 前后缀分解和双指针两种写法详解

42. 接雨水 (Trapping Rain Water)

题目来源

42. 接雨水

题目分析

在这道题目中,我们需要计算给定高度图中能接住多少雨水。题目描述如下:

题目: 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

题目难度

  • 难度:困难

题目标签

  • 标签:数组、双指针、动态规划、栈

题目限制

  • n == height.length
  • 0 <= n <= 2 * 10^4
  • 0 <= height[i] <= 10^5

解题思路

这道题的核心思路是找到每个柱子能容纳多少雨水。容纳的水量取决于其左右两边柱子的最大高度。每个柱子容纳的水量=左和右两边柱子最大高度中的最小值(木桶原理)- 每个柱子的高度

方法一:前后缀分解

首先,我们可以通过计算每个柱子左边的最大高度和右边的最大高度来确定当前柱子所能容纳的水量。具体步骤如下:

  1. 计算前缀最大值pre_max[i] 记录 i 之前(包括 i)的最大值。
  2. 计算后缀最大值suf_max[i] 记录 i 之后(包括 i)的最大值。
  3. 计算水量:对于每个柱子,可以接的水量为 min(pre_max[i], suf_max[i]) - height[i],并累加得到总水量。
方法二:双指针

双指针方法是对前后缀分解方法的优化。我们不需要单独记录前缀和后缀的最大值,而是通过两个指针从数组的两端向中间移动,动态更新当前左右两边的最大高度。记录前缀最大值pre_max和后缀最大值suf_max

因为对于每个柱子来说,是要找前缀最大值pre_max和后缀最大值suf_max中的较小值,所以当当前的前缀最大值pre_max小于后缀最大值suf_max时,我们已经可以确定left指针指向的柱子的min(pre_max[i], suf_max[i]) 可以计算水量了,同理,当前缀最大值pre_max大于后缀最大值suf_max时,我们可以确定right指针指向的柱子的min(pre_max[i], suf_max[i])

  1. 初始化指针left 从左边开始,right 从右边开始。
  2. 更新最大值:分别更新 leftright 所在位置的最大高度。
  3. 计算水量:如果左边最大值小于右边最大值,则计算左边柱子的水量,并将 left 向右移动;否则计算右边柱子的水量,并将 right 向左移动。
  4. 返回结果:最终累加所有柱子上的水量。

代码实现

方法一:前后缀分解
public int trap(int[] height) {
    int n = height.length;
    int ans = 0;
    int[] pre_max = new int[n];
    int[] suf_max = new int[n];
    
    pre_max[0] = height[0];
    suf_max[n - 1] = height[n - 1];
    
    for (int i = 1; i < n; i++) {
        pre_max[i] = Math.max(pre_max[i - 1], height[i]);
    }
    
    for (int i = n - 2; i >= 0; i--) {
        suf_max[i] = Math.max(suf_max[i + 1], height[i]);
    }
    
    for (int i = 0; i < n; i++) {
        ans += Math.min(pre_max[i], suf_max[i]) - height[i];
    }
    
    return ans;
}
方法二:双指针
public int trap2(int[] height) {
    int n = height.length;
    int ans = 0;
    int left = 0, right = n - 1;
    int pre_max = 0, suf_max = 0;
    
    while (left <= right) {
        pre_max = Math.max(pre_max, height[left]);
        suf_max = Math.max(suf_max, height[right]);
        
        if (pre_max < suf_max) {
            ans += pre_max - height[left];
            left++;
        } else {
            ans += suf_max - height[right];
            right--;
        }
    }
    
    return ans;
}

代码解读

  • 前后缀分解方法:时间复杂度为 O(n),空间复杂度为 O(n)。先分别计算每个柱子的左右最大高度,然后计算每个柱子能容纳的水量。
  • 双指针方法:时间复杂度为 O(n),空间复杂度为 O(1)。通过双指针动态更新左右最大高度,并计算水量。

测试用例

以下是几个测试用例,用于验证代码的正确性:

public static void main(String[] args) {
    int[] height1 = {0,1,0,2,1,0,1,3,2,1,2,1};
    int[] height2 = {4,2,0,3,2,5};
    
    System.out.println(trap(height1)); // 输出: 6
    System.out.println(trap2(height2)); // 输出: 9
}

性能分析

  • 时间复杂度O(n),其中 n 是高度数组的长度。
  • 空间复杂度
    • 前后缀分解方法O(n)
    • 双指针方法O(1)

总结

本题通过计算柱子的左右最大高度来确定能接多少雨水。前后缀分解方法较为直观,而双指针方法在空间复杂度上更具优势。选择合适的解法可以更高效地解决问题。


  • 14
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值