最大矩形面积问题——单调栈法
直方图最大矩形面积
-
给定非负整数数组 heights ,数组中的数字用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
示例1
输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10
示例2
输入: heights = [2,4]
输出: 4
分析
-
矩形的面积就是宽x高,因此只要能确定每个举行的宽和高,就能计算出它的面积。
-
宽可以通过数组下标相减的方式获得,至于高,只需要找对应范围的数组中的最小值即可得到,由此便可以得到面积
-
该类题型有三种解法:暴力破解、分治法、单调栈法。
- 暴力破解:
- 即便利所有的矩形,同时维护一个表示最大面积的变量即可
- 分治法:
- 观察示例1可以发现,直方图的最大矩形有以下三种可能:
- 1、矩形通过最矮的柱子,宽是最大的状态下。
- 2、起始的柱子和终止的柱子始终在最矮的柱子的左侧。
- 3、起始的柱子和终止的柱子始终在最矮的柱子的右侧。
- 于是可以有如下思路:找到最矮的柱子,分别计算这三种状态,其中23本质上是同一种情况,即将一个区域分成左右两遍,求最大矩形,如此便是两个相同的子问题,可递归求解。
- 观察示例1可以发现,直方图的最大矩形有以下三种可能:
- 单调栈法:
- 见下节。
- 暴力破解:
单调栈法
- 所谓单调栈法就是通过单调栈找到某一条柱子左右两边第一个小于它的柱子的下标,以此来计算当该条柱子位最短边时的最大矩阵的大小,当我们找遍了所有的柱子之后,也就覆盖了所有取得最小的可能情况。
- 能做到这种效果的栈叫单调栈,栈内保持单调递增,就能保证,栈顶所对应的柱子的左侧第一个比他小的柱子一定是它的下一个元素,如此以来,当我们碰到第一个比栈顶小的柱子的时候就可以得到以栈顶柱子为最小边的时候的最大矩形的区域,由此可以得到他的面积。
- 这个思想其实和分治法有一丢丢类似,即找所有以当前柱子为最短边的最大矩形(分治是不断分边界,然后重复情况1计算最大矩形),再从中维护一个最大值即可得到一个全局最大矩形。
过程示例
-
注:下面的图是来自:逗比克星
-
-
可以发现,最后栈中除了为了处理方便而加入的初始化的 -1 以外,还有 1, 4, 5 三个元素,说明以 1, 2, 3 柱高为顶的最大矩阵还未计算。这时候只要多想一步,它们未被计算是因为还未出现比它们矮的柱子。若假设最后再加入一个高度为 0 的柱高,那么栈中除了 -1 以外,所有的元素都会出栈,那么所有的柱子均被计算,计算完毕,继续的过程如下
-
代码示例
- 代码中我向数组两侧各插入一个比全局最小值还小的数字用以统一边界情况
- 注意面积计算部分,高为:heights[tmp],宽为:(i - s.top() - 1),意思是找到不包含i和s.top()这两根柱子的中间区域作为宽
class Solution
{
public:
int largestRectangleArea(vector<int> &heights)
{
// 单调递非减栈
stack<int> s;
heights.insert(heights.begin(), 0);
heights.push_back(0);
int len = heights.size();
int maxArea = heights[0];
s.push(0);
for (int i = 1; i < len; i++)
{
while (heights[i] < heights[s.top()])
{
int tmp = s.top();
s.pop();
maxArea = max(maxArea, heights[tmp] * (i - s.top() - 1));
}
s.push(i);
}
return maxArea;
}
};
矩阵中最大的矩形
-
给定一个由
0
和1
组成的矩阵matrix
,找出只包含1
的最大矩形,并返回其面积。**注意:**此题
matrix
输入格式为一维01
字符串数组。
示例 1:
输入:matrix = ["10100","10111","11111","10010"]
输出:6
解释:最大矩形如上图所示。
示例 2:
输入:matrix = []
输出:0
示例 3:
输入:matrix = ["0"]
输出:0
示例 4:
输入:matrix = ["1"]
输出:1
示例 5:
输入:matrix = ["00"]
输出:0
提示:
rows == matrix.length
cols == matrix[0].length
0 <= row, cols <= 200
matrix[i][j] 为 '0' 或 '1'
分析
- 乍一看本题和直方图最大矩形面积似乎没有什么关系,但是换个角度看(逐行遍历,然后画坐标轴,就可以得到类似直方图样子,于是就可以转换成上一题的解法):
- 注:下面的图是来自cdx
代码示例
- 于是可以嵌套上一题的解法。
- 主体函数maximalRectangle来将问题转化为直方图中的最大矩形问题,利用上题largestRectangleArea函数求解。
- 这里在申请的时候直接一次性申请多两个空间,用以避免数组插入多情况出现。
class Solution
{
public:
int maximalRectangle(vector<string> &matrix)
{
//利用行的迭代,来转化为直方图中的最大矩形问题
int maxArea = 0;
int row = matrix.size();
if (row == 0)
return 0;
int col = matrix[0].size();
vector<int> heights(col + 2, 0);
for (int i = 0; i < row; i++)
{
for (int j = 1; j < col + 1; j++)
{
if (matrix[i][j - 1] == '0')
heights[j] = 0;
else
heights[j] += 1;
}
maxArea = max(maxArea, largestRectangleArea(heights));
}
return maxArea;
}
int largestRectangleArea(vector<int> &heights)
{
// 单调递非减栈
stack<int> s;
// heights.insert(heights.begin(), 0);
// heights.push_back(0);
int len = heights.size();
int maxArea = heights[0];
s.push(0);
for (int i = 1; i < len; i++)
{
while (heights[i] < heights[s.top()])
{
int tmp = s.top();
s.pop();
maxArea = max(maxArea, heights[tmp] * (i - s.top() - 1));
}
s.push(i);
}
return maxArea;
}
};