Const
l=i-1
,r=i+1
。
width = (r-1) - (l+1) + 1
,计算当前围的矩形面积,更新最大矩形面积。暴力做法的本质是:向左跑找第一个小于当前柱子高度的位置,向右跑找第一个小于当前柱子高度的位置。那么如果我们枚举hs[i]
的时候,能够迅速知道hs[i]
的左右两边第一个小于hs[i]
的位置,那么我们就可以获取hs[i]
对应的矩形的宽度了从而计算矩形面积了。于是问题转化成了两个子问题:
hs[i]
的右边第一个小于hs[i]
的位置。hs[i]
的左边第一个小于hs[i]
的位置。
因此我们想到可以使用单调栈来处理这两个子问题。我们维护一个从栈底到栈顶单调递增栈,栈中存储的是柱子的下标:找第一个更大/更小的元素,可以考虑使用单调栈,从栈底到栈顶的顺序:
// 单调栈解法
function largestRectangleArea(hs: number[]): number {
const n = hs.length;
let st: number[] = [];
const lSt: number[] = new Array(n).fill(-1);
const rSt: number[] = new Array(n).fill(n);
// 找每个数右边第一个比它小的位置
for (let i = 0; i < n; i++) {
while (st.length && hs[st[st.length - 1]] > hs[i]) {
const j = st.pop()!;
rSt[j] = i;
}
// 如果栈为空,说明左边没有比它小的位置
if (st.length !== 0) {
lSt[i] = st[st.length - 1];
}
st.push(i);
}
// 至此我们找到了所有柱子左边第一个比它小的位置,右边第一个比它大的位置。
// 我们开始计算面积
// 相对于暴力,我们相当于预处理了当前柱子的左右边界
let ans = hs[0];
for (let i = 0; i < n; i++) {
let l = lSt[i];
let r = rSt[i];
const w = (r - 1) - (l + 1) + 1;
const h = hs[i];
ans = Math.max(w * h, ans);
}
return ans;
};
// 暴力解法(超时)
function largestRectangleArea2(hs: number[]): number {
const n = hs.length;
let ans = hs[0];
// 枚举当前高度,假设当前高度是构成一个矩形的高度,不断向左右扩展寻找宽度
for (let i = 0; i < n; i++) {
let l = i - 1;
let r = i + 1;
// 如果每次观察到l所在位置的高度>=枚举到的高度,那么l往左跑一步
while (l >= 0 && hs[l] >= hs[i]) {
l--;
}
// 如果每次观察到r所在位置的高度>=枚举到的高度,那么r往右跑一步
while (r < n && hs[r] >= hs[i]) {
r++;
}
// 当l和r都跑到不符合条件后,则width = (r-1) - (l+1) + 1
ans = Math.max(((r - 1) - (l + 1) + 1) * hs[i], ans);
}
return ans;
};
84.柱状图中最大的矩形
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
示例 1:
输入:
heights = [2,1,5,6,2,3]
输出:
10
解释: 最大的矩形为
5,6
这两根柱子构成的面积为2*5=10
。示例 2:
heights = [2,4]
输出:
4
提示:
1 <= heights.length <= 10^5
0 <= heights[i] <= 10^4