> 技术文档 > 计算柱状图中最大的矩形【单调栈】

计算柱状图中最大的矩形【单调栈】


给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
示例 1:
输入:heights = [2,1,5,6,2,3]
输出:10
示例 2:
输入: heights = [2,4]
输出: 4
LeetCode链接:https://leetcode.cn/problems/largest-rectangle-in-histogram/description/

暴力思路

        最先想到的也是最简单的思路,遍历列表计算每个高度的最大区域,但是无法在规定时间内处理105级别的数据。和常规的暴力解法不同,我先对列表中的元素进行了一轮排序,然后从大到小进行查找,同时跳过数值相同的元素,可以在一定程度上减少处理时间。

class Solution {public: int largestRectangleArea(vector<int>& heights) { int maxArea = 0; int hSize = heights.size(); int w = 0, _w = 0; // 辅助 vector<int> tmp(heights); // 存储不同最大最小高度的面积 vector<int> allArea = {}; sort(tmp.begin(), tmp.end()); allArea.push_back(tmp[0] * hSize); for (int i = 1; i < hSize; i++) { if (tmp[i] == tmp[i - 1]) continue; int _j = -1; bool flag = false; for (int j = 0; j < hSize; j++) { // 从大于tmp[i]的地方开始 if (tmp[i] <= heights[j]) {  if (!flag) { _j = j; flag = true;  } } // j 比当前小, 或者j到末尾 if (flag && ((tmp[i] > heights[j]) || j == (hSize - 1))) {  w = j - _j;  if (j == (hSize - 1) && (tmp[i] <= heights[j])) w++;  if (_w < w) _w = w;  flag = false; } } //_w 为以tmp[i]做最小高度的最长区间 allArea.push_back(tmp[i] * _w); _w = 0; } for (int i = 0; i < allArea.size(); i++) { // cout << allArea[i] << \' \'; if (maxArea < allArea[i]) maxArea = allArea[i]; } return maxArea; }};

提交后显示超出时间限制
计算柱状图中最大的矩形【单调栈】

单调栈思路

单调栈

        单调栈是对栈的一种使用方法并不是固定的数据结构,通过比较当前元素和栈顶元素之间的大小关系进行入栈、计算、出栈等操作,有单增栈和单减栈两种用法。本题使用的是单增栈。
        求解最大矩形区域可以视作求解每个元素的最大区域,然后(或并)选取其中最大的。以输入[2,1,5,6,2,3]为例,求解过程为先计算第一个元素2的最大区域易得2,再计算第二个元素1的最大区域。。。直到最后一个元素,可以得到最大区域列表[2, 6, 10, 6, 8, 3]。最大区域由高度和宽度两方面组成,其中宽度由左右两端第一个小于当前元素的元素下标决定。可以通过在单调栈中存储单增序列的下标实现通过对数组的一次遍历,求得最大区域,达到*O(n)*的时间复杂度。以[2,1,5,6,2,3]为例,具体过程如下:

单调递增栈操作 1、S1[栈状态] --> T1[0] 2、T1 --2<1? 弹出0--> T2[] 3、T2 --> 压入1 4、S2[:1] --5>1?--> 压入2 5、S3[:1,2] --6>5?--> 压入3 6、S4[:1,2,3] --2<6? 弹出3--> 计算6的最大区域 7、S5[:1,2] --2<5? 弹出2--> 计算5的最大区域 8、S6[:1] --2>1?--> 压入4 9、S7[:1,4] --3>2?--> 压入5 10、S8[:1,4,5] --0>3? 弹出5--> 计算3的最大区域 11、S9[:1,4,5] --0>2? 弹出4--> 计算2的最大区域 12、S10[:1,4,5] --0>1? 弹出1--> 计算1的最大区域 end

        使用单调栈时,主要分两种操作:
1、入栈。当当前元素大于栈顶元素(或栈为空)时,将当前元素下标入栈。
2、出栈并计算最大区域。当栈不为空,且当前元素小于栈顶元素时,进行出栈操作,计算栈顶元素的区间,并比较存储区域面积。如果栈顶元素依旧大于当前元素,则继续进行计算,直到栈顶元素小于当前元素(步骤8),或栈为空(步骤2)。计算面积时,高度固定,宽度需要计算,此时存在两种情况,栈为空和栈不为空,其中:栈不为空时,l表示左侧小于当前元素的位置使用r-l-1;栈为空时表示左侧没有元素大于当前元素,此时区间的长度为当前r所在的位置,l的值为需为-1。当前l左侧不为空时,需要继续计算后续元素的最大区间,因此需要出栈栈顶元素,并进行新一轮比较,判断需要入栈还是出栈,最后的中止条件为遍历整个数组输入的元素。
        此外,为了保证右侧有终止,需要在输入数组的尾部补0,用作右下界。

根据以上逻辑,可得以下代码:

class Solution {public: int largestRectangleArea(vector<int>& heights) { int Res = 0; stack<int> length; heights.push_back(0);for (int r = 0; r < heights.size(); r++) {while (!length.empty() && heights[r] <= heights[length.top()]) {int h = heights[length.top()];length.pop();int l = length.empty() ? -1 : length.top();Res = max((r - l - 1) * h, Res);}length.push(r);} return Res; }};

执行结果:
计算柱状图中最大的矩形【单调栈】