这是我参与11月更文挑战的第12天,活动详情查看:2021最后一次更文挑战
这个季节的下午,困的不行,刷个算法题解解困吧
题目
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的直方图,在这种情况下,可以接 6 个单位的水(蓝色部分表示水)。
示例1
输入:height = [4,2,0,3,2,5]
输出:9
思路
想要计算整个直方图能够储存的水量,可以分解为分别计算每一个位置i能够储存的水量,然后将每个位置i的水量加起来,就能得到结果。
通过分析上图,可以得出,想要计算位置i的储水量,需要找到位置i左边直方体的最大值 l_max,再找到右边直方体的最大值 r_max,取两者 最小值,然后减去位置i 的高度 height[i],就可以得到位置i的储水量
边界判断: 当直方体的数量小于2(height.length < 2)的时候,没有必要再去计算,因为构不成储水区。
解答
解法1:暴力解法
按照思路的步骤,很容易写出:
// 暴力法
var trap = function(height) {
if(height.length < 2) return 0;
const n = height.length;
let ans = 0;
for(let i = 1; i < n - 1; i++) {
let l_max = 0, r_max = 0;
for(let j = 0; j <= i; j++) {
if(height[j] > l_max) {
l_max = height[j];
}
}
for(let k = i; k < n; k++) {
if(height[k] > r_max) {
r_max = height[k];
}
}
ans += Math.min(l_max,r_max) - height[i];
}
return ans;
};
解法2:动态规划(优化时间复杂度)
思考:在上述解法中,第一层循环内,每次都会重复计算很多 l_max 和 r_max,很明显,我们可以先把每个位置的 l_max 和 r_max 提前计算出来,减少时间复杂度
可以定义 l_max 、r_max 为数组,l_max[i] 为 位置i 左边的最高直方体高度,r_max[i] 为 位置i 右边的最高直方体高度
// 优化时间复杂度
var trap = function(height) {
if(height.length < 2) return 0;
const n = height.length;
let ans = 0;
let l_max = new Array(n - 1).fill(0);
let r_max = new Array(n - 1).fill(0);
l_max[0] = height[0];
r_max[n - 1] = height[n - 1];
for(let i = 1; i < n - 1; i++) {
l_max[i] = Math.max(height[i], l_max[i - 1]);
}
for(let i = n - 2; i >= 0; i--) {
r_max[i] = Math.max(height[i], r_max[i + 1])
}
for(let i = 1; i < n - 1; i++) {
ans += Math.min(l_max[i], r_max[i]) - height[i]
}
return ans;
};
解法3:双指针
思考: 维护两个指针 left 和 right,以及两个变量 l_max 和 r_max,初始时 left=0,right=n-1,l_max = height[0],r_max = height[n - 1]。指针 left 只会向右移动,指针 right 只会向左移动,在移动指针的过程中维护两个变量 l_max 和 r_max 的值
// 双指针
var trap = function(height) {
if(height.length < 2) return 0;
const n = height.length;
let ans = 0;
let l_max = height[0];
let r_max = height[n - 1];
let left = 0;
let right = n - 1;
while(left <= right) {
l_max = Math.max(l_max, height[left]);
r_max = Math.max(r_max, height[right]);
if(l_max < r_max) {
ans += l_max - height[left];
left++;
} else {
ans += r_max - height[right];
right--;
}
}
return ans;
};