[力扣] 系列是我个人总结的力扣上的一些算法题,主要记录思路和心得,因为最近在准备面试,所以会使用JavaScript来替代Java来实现算法。
注意:本题双指针的思路借鉴于:【双指针,按行求解】相信我,真的真的是非常好懂的思路!
给定一个直方图(也称柱状图),假设有人从上面源源不断地倒水,最后直方图能存多少水量?直方图的宽度为 1。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/evaluate-reverse-polish-notation
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
在讲思路前说一点题外话,这道题是我美团三面的原题,当时没做出来。(还是太菜了55)
面试时的思路如下:
雨水应该要在波谷中接收,所以可以找出所有的peek(波峰),之后两两峰配对,计算:小的峰值minPeek-height[i]
统计入结果即可。
此种方法需要遍历两次数组,第一次找出peek,第二次配对peek,然后计算区间内雨水。常数次遍历数组,时间复杂度:O(n);
面试时没有想出别的办法,基本上最后还是写完了的,由于时间关系没有来得及调试。
面试官笑着说,如果后续这种思路做出来了,让我通过HR发给他。(当时内心OS:完蛋了,没做出来被刷了,可能这种思路行不通)
面试完我立刻去力扣上找了原题,这道题合理的数据结构是:动态规划、单调栈、双指针,这三种。我的思路一个是朴素的迭代,另外一个是滑动窗口,都不符合这道题的解法。
我的思路的问题在于:
如果存在这样一种情况:最两侧波峰peek最高,区间内出现了若干个peek,我的逻辑只会统计到下图中1,2,2,2,2 最低的那几个1和2的体积,忽视了两侧树立起的高峰。
实际上由于10和8 这两个peek存在,之间的区域都可以填充到8的水平线。我之前的思路是无法体现出两侧的最大值这个概念的。
接下来我讲讲我对于这道题比较倾向的两种思路吧。
思路:对每一个元素,向两侧分别寻找方向上的最大值,然后取两者中较小的一个minPeek, 再计算 minPeek - height[i]
即可。
细节 及 处理方法:
minPeek > height[i]
的元素。var trap = function(height) {
let ans = 0;
for (let i = 0; i < height.length; i++) {
let minP = minPeek(height, i);
if (minP > height[i]) {
ans += minP - height[i];
}
}
return ans;
};
// 寻找左侧和右侧最大值中较小的一个
var minPeek = function(height, index) {
let i = index;
let j = index;
let leftMaxValue = 0;
let rightMaxValue = 0;
while (i >= 0) {
leftMaxValue = Math.max(leftMaxValue,height[i]);
i--;
}
while (j < height.length) {
rightMaxValue = Math.max(rightMaxValue,height[j]);
j++;
}
return Math.min(leftMaxValue,rightMaxValue);
}
此解法的思路是借鉴文章开头提到的题解。
思路:将整个数组的壁 与 容积视为一个容器,计算出全部体积(黑色与蓝色部分),再对数组求和(黑色部分),然后用全部体积 减 数组的和就剩下了蓝色部分。
数组求和比较简单,关键在于如何求出全部体积。
我们把它分层求,每一层的高度为 h,第一层为1 第二层为2…
只要h > height[left]或者height[right],左右指针left 和right 就不断向中间移动。
第一层:left = 1, right = 11 停下,体积v1 = right - left + 1 = 11
第二层:left = 3, right = 10 停下,体积v2 = right - left + 1 = 8
第三层:left = 7, right = 7 停下, 体积v3 = right - left + 1 = 1
此时我们停止循环。
综上所述,循环条件应该是 left <= right,在第三层只有一个的时候我们仍要统计。
小细节:实现时,内循环也要写 left <= right
这个条件,不写的话如果两个指针在某次循环结束后相等的话,进入循环并在第一次left++后,比较的h是永远不会大于或等于元素值的,因为两个指针已经错过了,h和之前在right指针处比较过的的值在比较。
var trap = function(height) {
let left = 0;
let right = height.length - 1;
let h = 1;//层数
let total = 0;//总容积
while (left <= right){
while (left <= right && height[left] < h) {
left++;
}
while (left <= right && height[right] < h) {
right--;
}
h++;
total += right - left + 1;
}
let blackPart = 0;//容器壁的体积
for (let i = 0; i < height.length; i++) {
blackPart += height[i];
}
return total - blackPart;
};
时间复杂度:O(n)。两个指针从两边遍历,我们只对数组遍历了一次。
空间复杂度:O(1)。使用了常数个变量。