当青训营遇上码上掘金」之主题三--“攒青豆”的三种解法。
- 暴力穷举法
- 备忘录优化时间复杂度
- 双指针-兜底解法
先列代码:
解题思路
- 着手具体,将大问题分解成小的,益处理的单元,再想如何能将这些单元结果链接在一起。
- 就像我们处理字符串问题,不要考虑如何处理整个字符串,而是思考如何处理每一个字符。
- 现位置 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;
}