当青训营遇上码上掘金--“攒青豆”的三种解法

132 阅读2分钟

当青训营遇上码上掘金」之主题三--“攒青豆”的三种解法。

  • 暴力穷举法
  • 备忘录优化时间复杂度
  • 双指针-兜底解法

先列代码:

解题思路

  • 着手具体,将大问题分解成小的,益处理的单元,再想如何能将这些单元结果链接在一起。
  • 就像我们处理字符串问题,不要考虑如何处理整个字符串,而是思考如何处理每一个字符。
  • 现位置 i 能承载的水滴取决于其左右最高柱子。
  • 计算机解决问题思路没有特殊技巧,唯一解决办法就是穷举。算法设计无非就是先思考“如何穷举”,然后再追求“聪明的穷举”。
  • 回想动态规划。虽然最优子结构不是动态规划独有的一种性质,但能求最值的问题大部分都有这个性质。最优子结构性质是动态规划问题的必要条件,一定是让你求最值的。所以碰到最值题,先思考暴力穷举,如果复杂度爆炸,思路就可以往动态规划上走。
  • 一个例子就是求全级10个班中找成绩最高的一个。我们从每个班找最高的,之后将它们再找一次最高的,全级最高就出来了。
  • 最优子结构就是从子问题的最优结果推出更大规模问题的最优结果。
  • 找最优子结构的过程,就是证明状态转移方程正确性的过程,方程符合最优子结构就可以写暴力解了,写出暴力解就可以看出有无重叠子问题,有则优化。

暴力穷举法

//暴力穷举法
var trap = function(height) {
    const n = height.length;
    var ans = 0;
    //i=1;i<n-1;使得遍历不会超出数组范围,前后各留一个。
    //对于数组中每个元素,遍历其左右最高的元素,之后选择最矮的一个减去当前高度,就可以获得现位置可以接到的雨水。
    for(let i=1; i < n-1; i++) {
        //每次移动现位置都将左右最高重置,不然移动到最高位置时,最高还没有变,于是原本在最高左右没有比它更高的,在逻辑上缺出现了一堵高墙。
        let l_max = height[0];
        let r_max = height[n-1];
        //遍历现元素左边最高的元素
        for(let j=i; j>=0; j--) {
            l_max = Math.max(height[j], l_max);
        }
        //遍历现元素右边最高的元素
        for (let j=i; j<n; j++) {
            r_max = Math.max(height[j], r_max);
        }
        ans = ans + Math.min(r_max, l_max) - height[i];
    }
    return ans;
};

备忘录优化法

var trap = function(height) {
    const n = height.length;
    const l_max_arr = [];
    const r_max_arr = [];
    var ans=0;
    l_max_arr[0] = height[0];
    r_max_arr[n-1] = height[n-1];
    //使用数组存储每个位置最左和最右最高的元素,起到备忘录作用。
    //从原本两层嵌套的 for 循环中解脱出来,使其只需要分别遍历一轮。
    //两两比较过程中可以获得一路过来中最高元素
    //习惯上让靠前的元素保持下标标识,之后的元素以其位置为标准
    for(let i=1; i<n; i++) {
        l_max_arr[i] = Math.max(height[i], l_max_arr[i-1]);
    }
    for(let j=n-2; j>=0; j--) {
        r_max_arr[j] = Math.max(height[j], r_max_arr[j+1]);
    }
    //注意,备忘录这里我不用考虑边界条件?k<n/k<n-1,k=0/k=1都无所谓?
    //这是因为,位于最左和最右的柱子都不需要考虑,不可能承载水滴的,最多只是边界高墙,还是由第二个元素的高度来决定,这时已经进入备忘录了。
    for(let k=0; k<n; k++) {
        ans = ans + Math.min(r_max_arr[k], l_max_arr[k]) - height[k];
    }
    return ans;
}

双指针解法

//双指针解法
//利用的是,兜底思想。
//我们只需要知道,有一边的高度足以支撑我们现在
/* var trap = function(height) {
    const n = height.length;
    let left = 0;
    let right = n-1;
    var ans = 0;
    let l_max = height[0];
    let r_max = height[n-1];
    while(left<right) {
        l_max = Math.max(height[left], l_max);
        r_max = Math.max(height[right], r_max);
        if(l_max < r_max) {
            ans = ans + l_max - height[left];
            left++;
        } else {
            ans = ans + r_max - height[right];
            right--;
        }
    }
    return ans;
}