一.题目如下
二.思路
1.起始的复杂思路:
有俩点是很容易想到的:
1.由俩条边决定雨水集蓄量,很容易想到双指针求解
2.雨水的集蓄量是由短边决定
进一步的可以想到,该题目的求解可以转化为寻找非严格递减和非严格递增区间
但在处理边界条件上很容易写错,这是一个很容易出错的点:
在处理右边界时候需要注意是否存在多个区间合并的问题,也就是w字形的区间如果按单个算会漏算体积;具体到代码上,也就是当找到的先减后增区间的右边界小于左边界,应当不断继续向后搜索找有没有大于当前右边界的新右边界、更新新右边界,并且直到找到第一个大于左边界的右边界时应当停止,这时就完成一次完整的寻找,这么说有点难理解的话见下图:
上图这种情况,我们可以找到第一个这样的区间,a-c,但这是不完整的,需要继续往后找找到f,
那么就完成了一次查找计算体积,之后再寻找下一个这种区间;
上图这种情况下则需找到e,才是完整的区间
/**
* @param {number[]} height
* @return {number}
*/
// 双指针
// 能接多少水是由矮边决定
// 转化为找单调减后单调增的区间,并以该段区间的短边为标准来计算积水(错误)
// 可能有几个区间合并的情况(如果右柱子小于左柱子,那么继续往右找,知道找到第一个大于等于左柱子或者便利完,根据情况开启下一轮寻找)(错误)
// 应该是找后面更高的柱子,不管是不是大于等于左柱子
var trap = function (height) {
let ret = findDecreaseIncreaseSubarrays(height)
return ret;
};
function findDecreaseIncreaseSubarrays(nums) {
let ret = 0
if (nums.length < 3) return ret; // 至少需要3个元素才能形成先减后增的子数组
let i = 0;
while (i < nums.length - 1) {
// 1. 寻找严格递减的起始点
if (nums[i] >= nums[i + 1]) {
const start = i; // 记录下降的起始索引
// 继续下降直到不再严格递减
while (i < nums.length - 1 && nums[i] >= nums[i + 1]) {
i++;
}
// 此时i是下降阶段的最后一个索引(最低点)
// 2. 检查是否开始严格递增
if (i < nums.length - 1 && nums[i] <= nums[i + 1]) {
// 继续递增直到不再严格递增
while (i < nums.length - 1 && nums[i] <= nums[i + 1]) {
i++;
}
// 此时i是递增阶段的最后一个索引
let end = i; // 子数组的结束索引
let more = end
// 当右柱子矮于左柱子时,往后试找比end更高的柱子,如果找到了和左柱子一样或高于左柱子就停下来
if (nums[end] < nums[start]) {
for (let j = end + 1; j < nums.length; j++) {
if (nums[j] > nums[more]) {
more = j
}
if (nums[j] >= nums[start])
break;
}
// 如果有找到更高的就给end作为新右柱子,移动下一轮寻找起点
if (more != end) {
end = more
i = end
}
}
// 计算积水
for (let j = start + 1; j < end; j++) {
if (Math.min(nums[start], nums[end]) - nums[j] > 0)
ret += Math.min(nums[start], nums[end]) - nums[j]
}
} else {
// 如果没有递增趋势,直接跳过
i++;
}
} else {
// 如果没有下降趋势,继续向后检查
i++;
}
}
return ret;
}
2.leetCode大神的思路:
leetcode.cn/problems/tr… 很简洁,数学思维求解,俩个整块-全部=交集,也就是积水;看了别人的代码,突然感觉自己有种元谋人的感觉。。。