这是我参与11月更文挑战的第9天,活动详情查看:2021最后一次更文挑战
前言
关于 LeetCode 数组类型题目的相关解法,可见LeetCode 数组类型题目做前必看,分类别解法总结了题目,可以用来单项提高。觉得有帮助的话,记得多多点赞关注哦,感谢!
题目描述
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
示例 1:
输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10
示例 2:
输入: heights = [2,4]
输出: 4
链接:leetcode-cn.com/problems/la…
题解
- 暴力解法。想法是每次以 heights[i] 为高,向左右扩展矩形,直到遇到比 heights[i] 小的值停止扩展,那么这个矩形的面积就为 heights[i] * 宽度。具体代码如下,这个方法时间复杂度为 O(n^2),会超时。
/**
* @param {number[]} heights
* @return {number}
*/
var largestRectangleArea = function(heights) {
const n = heights.length
let max = -1
for (let i = 0; i < n; i++) {
let left = right = i
let height = heights[i]
while (left - 1 >= 0 && heights[left - 1] >= height) {
--left;
}
while (right + 1 < n && heights[right + 1] >= height) {
++right;
}
max = Math.max(max, (right - left + 1) * height);
}
return max
};
- 单调栈。上面的方法每次一个高度,都需要遍历左右来找边界。我们可以利用单调栈的性质来记录边界。单调栈顾名思义,就是单调递增/递减的栈,满足栈里的元素是单调增/减的。这里我们利用单调递增栈,当一个元素入栈时,可以判断栈顶元素和自己的大小,如果比自己小,那么,说明以自己为高的矩形左边界就是栈顶元素的下标,自己入栈;如果比自己大,就把栈顶元素 pop 出去,直到找到比自己小的元素,记录比自己小的元素的下表,作为边界。
这样,我们通过从左到右构造单调栈可以记录左边界,从右到左构造单调栈记录右边界,最后计算矩形的面积。具体代码如下,时间复杂度 O(n)
/**
* @param {number[]} heights
* @return {number}
*/
var largestRectangleArea = function(heights) {
const n = heights.length
let left = new Array(n)
let right = new Array(n)
let stack = []
for (let i = 0; i < n; i++) {
while(stack.length && heights[stack[stack.length - 1]] >= heights[i]) {
stack.pop()
}
left[i] = stack.length === 0 ? -1 : stack[stack.length - 1]
stack.push(i)
}
stack = []
for (let i = n - 1; i >= 0; i--) {
while(stack.length && heights[stack[stack.length - 1]] >= heights[i]) {
stack.pop()
}
right[i] = stack.length === 0 ? n : stack[stack.length - 1]
stack.push(i)
}
let ans = 0;
for (let i = 0; i < n; ++i) {
ans = Math.max(ans, (right[i] - left[i] - 1) * heights[i]);
}
return ans;
};
- 单调栈优化。很容易注意到,在我们从左到右构造递归栈时,其实右边界也同时确定了,我们不需要再从右到左遍历一遍数组来记录右边界。
需要注意的是right数组元素需要默认为 n。
代码如下
/**
* @param {number[]} heights
* @return {number}
*/
var largestRectangleArea = function(heights) {
const n = heights.length
let left = new Array(n)
let right = new Array(n).fill(n)
let stack = []
for (let i = 0; i < n; i++) {
while(stack.length && heights[stack[stack.length - 1]] >= heights[i]) {
right[stack[stack.length - 1]] = i
stack.pop()
}
left[i] = stack.length === 0 ? -1 : stack[stack.length - 1]
stack.push(i)
}
let ans = 0;
for (let i = 0; i < n; ++i) {
ans = Math.max(ans, (right[i] - left[i] - 1) * heights[i]);
}
return ans;
};