接雨水

接雨水经典题:可用双指针或单调栈;单调栈做法把弹出的柱子视为凹槽底部。

#resource / algorithm #type / snippet #status / growing #source / leetcode #algo / two-pointers #algo / monotonic-stack #ds / stack

[!info] related notes 算法面试题型 MOC 单调栈

接雨水

题目链接:https://leetcode.cn/problems/trapping-rain-water/

题型判断

这题有两种主流做法:

  • 双指针
  • 单调栈

如果从栈角度理解,它属于“单调栈 + 凹槽结算”题。

单调栈思路

维护一个按高度单调递减的栈,栈里放下标。

当当前柱子 height[i] 比栈顶更高时,说明出现了右边界,可以尝试结算一段雨水。

弹出中间低柱 mid 后:

  • 新栈顶 left 是左边界
  • 当前 i 是右边界
  • mid 是凹槽底部

可接水高度:

min(height[left], height[i]) - height[mid]

宽度:

i - left - 1

双指针思路

双指针做法的核心是:

  • 哪一侧更低,就移动哪一侧
  • 移动时用该侧历史最高值结算当前位置能接的水

这是这题最常用的面试写法。

分析

同样是哪边的柱子高度小就移动哪边。 移动的时候计算新位置这一列能接多少雨水。移动的这一侧需要维护一个历史最大高度,再用这个高度减去当前位置高度。

function trap(height: number[]): number {
    const n = height.length;
    let left = 0, right = n - 1;
    let leftMax = height[left], rightMax = height[right];
    let ans = 0;
    while (left < right) {
        if (height[left] <= height[right]) {
            left++;
            leftMax = Math.max(leftMax, height[left]);
            const count = leftMax - height[left];
            if (count > 0) ans += count;
        } else {
            right--;
            rightMax = Math.max(rightMax, height[right]);
            const count = rightMax - height[right];
            if (count > 0) ans += count;
        }
    }
    return ans;
}

单调栈 JavaScript

var trap = function(height) {
    const stack = [];
    let ans = 0;

    for (let i = 0; i < height.length; i++) {
        while (stack.length && height[i] > height[stack[stack.length - 1]]) {
            const mid = stack.pop();

            if (!stack.length) break;

            const left = stack[stack.length - 1];
            const h = Math.min(height[left], height[i]) - height[mid];
            const w = i - left - 1;
            ans += h * w;
        }

        stack.push(i);
    }

    return ans;
};

一句话总结

单调栈做法里,被弹出的柱子不是边界,而是凹槽底部;左右边界由弹栈后的栈顶和当前柱子共同决定。

创建于 2026/3/5 更新于 2026/5/27