【LeetCode】42. 接雨水

leetcode

 

解题思路

接雨水问题的核心在于计算每个位置能承载的雨水量,这取决于该位置左侧最高柱子和右侧最高柱子中较矮的那个​(木桶效应)。以下是三种主流解法及双指针法的优化实现:

  1. 暴力解法:每个位置遍历左右两侧找最高柱子,时间复杂度 O(n²),空间复杂度 O(1)。
  2. 动态规划:预处理左右最大值数组,时间复杂度 O(n),空间复杂度 O(n)。
  3. 双指针法:动态维护左右指针和极值,时间复杂度 O(n),空间复杂度 O(1)。

双指针法是最优解,通过左右指针向中间移动,每次处理较矮的一侧,实时更新极值并计算水量。


关键步骤

  1. 初始化变量

    • left 和 right 指针分别从数组两端开始。
    • leftMax 和 rightMax 记录左右遍历过程中的最大高度。
    • water 累加总雨水量。
  2. 移动策略

    • 比较 height[left] 和 height[right],选择较矮的一侧处理。
    • 若当前高度小于对应侧的极值,计算雨水量;否则更新极值。
  3. 终止条件:当左右指针相遇时结束循环。


Golang代码实现

package main

import "fmt"

func trap(height []int) int {
    n := len(height)
    if n <= 2 {
        return 0 // 边界处理:至少需要3根柱子才能形成凹槽
    }

    left, right := 0, n-1    // 双指针初始化
    leftMax, rightMax := 0, 0
    water := 0

    for left < right {
        // 选择较矮的一侧处理(木桶效应)
        if height[left] < height[right] {
            if height[left] > leftMax {
                leftMax = height[left] // 更新左侧最高挡板
            } else {
                water += leftMax - height[left] // 当前列积水量
            }
            left++
        } else {
            if height[right] > rightMax {
                rightMax = height[right] // 更新右侧最高挡板
            } else {
                water += rightMax - height[right] // 当前列积水量
            }
            right--
        }
    }
    return water
}

代码解析

  • 双指针策略:左右指针向中间移动,每次处理较矮的一侧,确保水量计算基于可靠的最小极值。
  • 极值更新leftMax 和 rightMax 动态更新,分别记录遍历过程中左右侧的最大高度。
  • 水量计算:若当前高度低于对应侧极值,累加 极值 - 当前高度 作为雨水量。

复杂度分析

指标说明
时间复杂度 O(n) 单次遍历数组
空间复杂度 O(1) 仅需常数级变量

运行示例

func main() {
    // 示例1验证
    height1 := []int{0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1}
    fmt.Println("示例1输出:", trap(height1)) // 输出: 6

    // 示例2验证
    height2 := []int{4, 2, 0, 3, 2, 5}
    fmt.Println("示例2输出:", trap(height2)) // 输出: 9
}

核心逻辑验证

以示例1 [0,1,0,2,1,0,1,3,2,1,2,1] 为例:

  1. 初始状态left=0right=11leftMax=0rightMax=1
  2. 左指针移动left=1(高度1),更新 leftMax=1,无积水。
  3. 右指针移动:处理高度1的右端,计算 rightMax 并累加水量。
  4. 最终累计:遍历至中间位置时,左右极值动态更新,计算所有凹槽的水量总和为6。

总结

双指针法通过动态维护极值和单次遍历,实现了时间和空间的最优解。其核心在于:

  1. 木桶效应:水量由较矮一侧的极值决定。
  2. 边界处理:无需预处理数组,直接通过指针移动计算。
posted @ 2025-03-27 12:38  云隙之间  阅读(90)  评论(0)    收藏  举报