🔥个人主页: Milestone-里程碑
❄️个人专栏: <<力扣hot100>> <<C++>><<Linux>>
🌟心向往之行必能至
题目描述
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
这是一道经典的困难级别题目,核心在于理解每个位置能承接的雨水量,是由它左右两侧的最高柱子中较矮的那根决定的。
双指针解法思路
核心原理
对于任意位置 i,能接的雨水量 = min(左侧最高高度, 右侧最高高度) - height[i]。如果该值为负,则说明无法接水,记为 0。
双指针优化
我们不需要提前计算左右两侧的最高高度数组(这会占用 O (n) 空间),可以用双指针在一次遍历中完成计算:
- 用
left和right两个指针分别从数组两端向中间移动 - 维护
pleft和pright两个变量,分别记录左指针左侧的最高高度和右指针右侧的最高高度 - 每次比较
pleft和pright,选择较小的一侧进行计算:- 如果
pleft < pright,说明左指针位置的雨水量由pleft决定,计算后左指针右移 - 否则,说明右指针位置的雨水量由
pright决定,计算后右指针左移
- 如果
完整代码
cpp
class Solution {
public:
int trap(vector<int>& height) {
int pleft = 0, pright = 0;
int left = 0;
int sum = 0;
int right = height.size() - 1;
while (left < right) {
pleft = max(pleft, height[left]);
pright = max(pright, height[right]);
sum += pleft < pright ? (pleft - height[left++]) : (pright - height[right--]);
}
return sum;
}
};
代码解析
-
初始化:
left指针从数组头部开始,right指针从数组尾部开始pleft和pright初始化为 0,分别记录左右两侧的最大高度sum用于累计总雨水量
-
双指针移动:
- 每次更新当前指针位置的最大高度
- 比较
pleft和pright,确定当前能计算的指针 - 计算当前指针位置的雨水量并累加到
sum - 移动对应的指针(左指针右移或右指针左移)
-
终止条件 :当
left >= right时,遍历结束,返回sum
复杂度分析
- 时间复杂度:O (n),每个元素最多被访问一次
- 空间复杂度:O (1),只使用了常数级别的额外空间
示例验证
以示例 1 输入 height = [0,1,0,2,1,0,1,3,2,1,2,1] 为例:
- 初始时
left=0,right=11 - 随着指针移动,
pleft和pright不断更新 - 最终累计
sum=6,与题目输出一致