这是我参与8月更文挑战的第11天,活动详情查看:8月更文挑战
大家好今天给大家分享下一道 LeetCode 困难难度 的题目接雨水
题目
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。(图片转载leetCode)
示例 1: 输入:height = [0,1,0,2,1,0,1,3,2,1,2,1] 输出:6 解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
输入:height = [4,2,0,3,2,5] 输出:9
分析
1.柱子宽度为1,柱子的高度都为整数,且可能有重复值
2.观察发现雨水的面积等于 两个柱子之间的宽度 *(两个柱子的最小值-洼地的高度)
3.返回雨水面积的总和
解法
1.stack
2.暴力法
解法一:stack
思路
1.stack维护一个单调递减的栈,迭代height,当height[i]遇到比stack顶大的就开始计算面积,步骤如下
1.栈顶出栈,作为洼地底部高度 top
2.剩下的栈顶作为洼地的边界
3.height[i]作为右边界
4.水池的高度为 Math.min(left,right)-top,这里面考虑了一种特性情况
1.因为可能有重复值,所以当left的高度和top的高度一样,通过计算那么高度就为0,面积也就为零
仅仅当left的高度大于top时候水池才会有高度
5.水池的宽度为 i-left索引-1
6.因为需要总面积所以需要累加
2.返回总面积
*/
var trap = function (height) {
// stack 存放索引
const stack = [];
let area = 0;
// 存放水池的的左边界
const left = [];
// 存放水池的右边边界
const right = [];
// 存放水池的底部高度
const bottom = [];
for (let i = 0; i < height.length; i++) {
//如果遇到stack的顶部元素小于新来的元素,说明遇到了水池的右边界
while (stack.length && height[stack[stack.length - 1]] < height[i]) {
//获取水池底部高度
const poolBottom = stack.pop();
// 但是如果stack为空,说明没有左边界,则结束循环
if (!stack.length) {
break;
}
//取得水池的 左边界索引
bottom.push(poolBottom);
//取得水池的 右边界索引
left.push(stack[stack.length - 1]);
//取得水池的底部索引
right.push(i);
}
stack.push(i);
}
// 迭代计算水池面积的总和
for (let i = 0; i < left.length; i++) {
const leftHeight = height[left[i]];
const rightHeight = height[right[i]];
const bottomHeight = height[bottom[i]];
// 取左右边界的最小值 - 底部的高度= 水池的蓄水高度
const accHeight = Math.min(leftHeight, rightHeight) - bottomHeight;
const width = right[i] - left[i] - 1;
area += accHeight * width;
}
return area;
};
/* 复杂度
时间 O(n)
空间 最大 O(n-2) 最小O(1)
*/
解法二:暴力法(无法通过 仅仅是提供思路)
思路
1.迭代没个柱子的高度 i
2.如果能找到比height[i]高的左右边界则表明可以蓄水,步骤如下
1.在i的左边寻找边界,如果有比height[i]高的,且是最高的左边界 则记录下来 leftHeight
2.在i的右边寻找边界,如果有比height[i]高的,且是最高的右边界 则记录下来 rightHeight
3.计算高度差 curHeight=(Math.min(leftHeight,rightHeight)-height[i])
4.计算面积 area = curHeight * 1(柱子高度)
3.重复上面的计算过程,在累加求出面积总和
*/
var trap = function (height) {
let area = 0;
for (let i = 0; i < height.length; i++) {
let leftHeight = -1;
// 求最高的左边界
for (let j = i; j > -1; j--) {
if (height[j] > height[i]) {
leftHeight = Math.max(height[j], leftHeight);
}
}
// 求最高右边界
let rightHeight = -1;
for (let j = i; j < height.length; j++) {
if (height[j] > height[i]) {
rightHeight = Math.max(height[j], rightHeight);
}
}
if (leftHeight === -1 || rightHeight === -1) {
continue;
}
const curHeight = Math.min(leftHeight, rightHeight) - height[i];
area += curHeight * 1;
}
return area;
};
/* 复杂度
时间 O(n^2)
空间 O(1)
*/
总结
这道题考察的是如果应用stack来寻找左右边界
大家可以看看我分享的一个专栏(前端搞算法)里面有更多关于算法的题目的分享,希望能够帮到大家,我会尽量保持每天晚上更新,如果喜欢的麻烦帮我点个赞,十分感谢
大家如果对“TS”感兴趣的可以看看我的专栏 (TypeScript常用知识),感谢大家的支持
文章内容目的在于学习讨论与分享学习算法过程中的心得体会,文中部分素材来源网络,如有侵权,请联系删除,邮箱 182450609@qq.com