题目要求:接雨水
给定 n
个非负整数表示每个宽度为 1
的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
解题思路:
联想到了我之前做过的另一道求盛水最多容器的题目,一眼看出使用双指针,通过同时从数组的两端向中间靠拢,利用两个指针分别比较当前左右指针指向的柱子高度,然后就直接求出接水量。但奈何本人能力有限也只知道要用双指针,具体如何实现完全没思路。于是乎只能考虑其他啥办法了。(没做过的可以先尝试做一下,可能做完之后就有思路了,参考链接:https://leetcode.cn/problems/container-with-most-water/description/?envType=study-plan-v2&envId=top-100-liked)
1.从左向右遍历法(思路错误,忽略解法1)
我的思路是从左往右遍历,通过找到每个柱子 i
之后第一个可以形成接水区域的柱子 j
(height[j] >= height[i]
),然后计算 i
和 j
之间的接水量。更新 i = j
,继续遍历,直到找到所有的接水区域。
实现代码
class Solution {
public:
int trap(vector<int>& height) {
int n = height.size();
if (n<3) return 0;
int res=0;
for (int i=0; i<n-1; i++){
for (int j=i+1; j<n; j++){
if (height[i]<=height[j]){
int minheight = min(height[i], height[j]);
for (int k=i+1; k<j; k++){
res += (minheight - height[k]);
}
i = j-1;
break;
}
}
}
return res;
}
};
其实错误的根源在于使用了 i = j - 1
来跳过位置 j
,并没有考虑像 [4, 2, 3]
这样的情况。这样会导致 i
没有正确处理 j
所在的接水区域,导致错误的结果。但是实在能力有限没有想到很好的方法去解决这个问题,看看之后能力变强之后能不能解决吧,先浅浅挖一个坑。
2.双指针法
具体思路:
(1)初始化指针和变量:
left
和 right
分别指向数组的两端,表示当前考察的左右两边的柱子。
left_max
和 right_max
分别记录从左边和右边看到的最高柱子的高度。
water
变量用于存储最终的积水量。
(2)核心逻辑:
使用 while (left < right)
,当左指针小于右指针时,循环继续。
比较 height[left]
和 height[right]
的值:
如果 height[left] < height[right]
,表示当前左边的柱子较矮,接水量由左边的 left_max
决定。如果当前 height[left] >= left_max
,更新 left_max
;否则计算积水量 left_max - height[left]
,然后左指针右移。
如果 height[left] >= height[right]
,则由右边的 right_max
决定接水量,计算方式类似,右指针左移。
(3)结束条件:
当 left
和 right
相遇时,所有的水都已经计算完毕,最终返回积水量 water。
具体代码:
class Solution {
public:
int trap(vector<int>& height) {
if (height.empty()) return 0; // 如果数组为空,直接返回 0
int left = 0, right = height.size() - 1; // 初始化双指针
int left_max = 0, right_max = 0; // 分别记录左边和右边的最高柱子
int water = 0; // 记录总的积水量
while (left < right) {
if (height[left] < height[right]) {
if (height[left] >= left_max) {
left_max = height[left]; // 更新左边最高柱子
} else {
water += left_max - height[left]; // 计算左边能接的水
}
++left; // 移动左指针
} else {
if (height[right] >= right_max) {
right_max = height[right]; // 更新右边最高柱子
} else {
water += right_max - height[right]; // 计算右边能接的水
}
--right; // 移动右指针
}
}
return water; // 返回总的积水量
}
};
双指针法通过同时从数组的两端向中间靠拢,利用两个指针分别维护左边和右边的最大高度。每次比较当前左右指针指向的柱子高度,并计算出可以接的水量。这种方法避免了不必要的重复计算,每个柱子最多访问一次。对比我的错误思路(如果可以实现的话),其效率高,时间复杂度 O(n),因为只需要遍历一次数组,两个指针分别从数组的两端向中间靠拢,每个位置最多访问一次;空间复杂度 O(1),只使用了常量的额外空间来存储指针和最大高度。每次根据左右指针的高度移动较小的一侧,动态更新左右两侧的最大高度,逐步计算接水量。