一、引言
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。求在该柱状图中,能够勾勒出来的矩形的最大面积。
来源:力扣(LeetCode)
二、单调栈
1、栈中维护数组的下标,栈中下标对应的数组值升序排列。栈中初始化-1结点,起始标记。
2、O(n)遍历数组中每个值,对第i个值有两种情况
(1)prev == -1 || heights[i] >= heights[prev]:下标i入栈
前项表示栈为空,后项表示满足单调性。
(2)heights[i] < heights[prev]:出栈
第i项违反单调性,栈中元素逐个出栈,直到栈顶元素满足单调性。每项出栈时,维护以出栈下标对应高度为高的最大矩形面积,出栈元素为prev,待入栈元素为i:
矩形高为heights[prev],左边界为栈中prev前一个元素(sta.top()),右边界为(i)。则矩形宽为(i-prev-1)。
3、遍历每个数组下标后,得到维护的最大面积,可以在数组最后加高为0的元素,即限制右边界,保障最终栈中元素全部退出。
核心思想:
每个下标prev出栈时计算以heights[prev]为高的最大矩形的面积。
可以发现:
该最大矩形的左边界为:栈中prev前一个下标(前一个下标对应的高度小于prev高度)
该最大矩形的右边界为:待入栈下标i(下标i对应的高度小于prev高度)
并且两个边界之间的下标对应的高度均大于prev高度。无论左右,比它高的下标在判断到它时均已出栈,所以找到的就是左右边界。
(1)左边界-prev间 比prev高的元素,在之前入栈prev时被出栈
(2)prev-右边界间 比prev高的元素,在当前出栈到prev时会被出栈
注意:对于连续多个下标的高度相等的情况,下文代码的最大矩形会以最左侧的下标考虑。(左边界找的是小于等于它高度的下标)(修改高度等于的情况可以调整)
三、程序代码
int largestRectangleArea(vector<int>& heights)
{
heights.push_back(0);
stack<int> sta;
int ans = 0;
sta.push(-1);
for (int i = 0; i < heights.size(); i++)
{
int prev = sta.top();
if (prev == -1 || heights[i] >= heights[prev]) //入栈
{
sta.push(i);
}
else // 出栈
{
while (prev != -1 && heights[prev] > heights[i])
{
sta.pop();
ans = max(ans, heights[prev] * (i - sta.top() - 1));
prev = sta.top();
}
sta.push(i);
}
}
return ans;
}