leetCode 42.接雨水总结

139 阅读3分钟

一.题目如下

Clipboard_Screenshot_1750941165.png

二.思路

1.起始的复杂思路:

有俩点是很容易想到的:

1.由俩条边决定雨水集蓄量,很容易想到双指针求解

2.雨水的集蓄量是由短边决定

进一步的可以想到,该题目的求解可以转化为寻找非严格递减和非严格递增区间

但在处理边界条件上很容易写错,这是一个很容易出错的点:

在处理右边界时候需要注意是否存在多个区间合并的问题,也就是w字形的区间如果按单个算会漏算体积;具体到代码上,也就是当找到的先减后增区间的右边界小于左边界,应当不断继续向后搜索找有没有大于当前右边界的新右边界、更新新右边界,并且直到找到第一个大于左边界的右边界时应当停止,这时就完成一次完整的寻找,这么说有点难理解的话见下图:

Clipboard_Screenshot_1750942747.png 上图这种情况,我们可以找到第一个这样的区间,a-c,但这是不完整的,需要继续往后找找到f, 那么就完成了一次查找计算体积,之后再寻找下一个这种区间;

Clipboard_Screenshot_1750942610.png

上图这种情况下则需找到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… 很简洁,数学思维求解,俩个整块-全部=交集,也就是积水;看了别人的代码,突然感觉自己有种元谋人的感觉。。。