LeetCode—42.接雨水[Trapping Rain Water]——分析及代码[C++]
一、题目
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/trapping-rain-water
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
二、分析及代码
1. 双指针法(初始版)
(1)思路
承接雨水的结构类似水槽,水面高度取决于左右两侧较低的木板,可以直观地联想到双指针法。
分别记录左右两侧当前已遍历的最高值,左右两侧各设置一个指针,每次选取最高值低者移动;
过程:
1)本侧最高值 >= 当前值:由于对侧一定存在更高值,当前格储水量即为 “本侧最高值 - 当前值”;
2)当前值 > 本侧最高值:当前值将成为新的水槽(不储水),将本侧最高值更新为当前值。
时间复杂度:一次遍历,O(n);
空间复杂度:O(1)。
(2)代码
class Solution {
public:
int trap(vector<int>& height) {
if (height.size() == 0) return 0;
int l = 0, r = height.size() - 1;
int lh = height[l], rh = height[r];
int ans = 0;
bool lmax = (lh >= rh) ? true : false;//记录当前最大值在左侧 or 右侧
while (l < r) {
if (lmax) {//左侧最高值更高,遍历右侧
r--;
if (height[r] <= rh)
ans += rh - height[r];
else {
rh = height[r];
if (height[r] > lh)
lmax = false;
}
}
else {//右侧最高值更高,遍历左侧
l++;
if (height[l] <= lh)
ans += lh - height[l];
else {
lh = height[l];
if (height[l] > rh)
lmax = true;
}
}
}
return ans;
}
};
(3)结果
执行用时 :8 ms, 在所有 C++ 提交中击败了 80.87% 的用户;
内存消耗 :9 MB, 在所有 C++ 提交中击败了 91.29% 的用户。
2. 双指针法(优化极简版)
(1)思路
进一步思考,可以直接根据指针对应值的高低选取遍历对象。
且由于每次都在左右指针中选取较低者进行遍历,所以存在以下特点:
1)未遍历部分(可具体到对侧)一定存在比已遍历部分(可具体到本侧)任意值都更高的值;
2)当且仅当指针移动后对应的新值大于已遍历的最高值时,左右指针才会更替,且对侧载入的值成为最高值(即 “本侧新值 > 对侧新值 > 原最高值” 时左右发生轮换);
因此,只需一个值记载已遍历的最大高度,无需分为左右两个值。
(2)代码
class Solution {
public:
int trap(vector<int>& height) {
int l = 0, r = height.size() - 1, h = 0, ans = 0;
while (l < r)
if (height[l] < height[r])
(height[l] >= h) ? (h = height[l++]) : (ans += h - height[l++]);
else
(height[r] >= h) ? (h = height[r--]) : (ans += h - height[r--]);
return ans;
}
};
(3)结果
执行用时 :4 ms, 在所有 C++ 提交中击败了 97.59% 的用户;
内存消耗 :9 MB, 在所有 C++ 提交中击败了 91.29% 的用户。
三、其他
暂无。