1、84.柱状图中最大的矩形
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 [2,1,5,6,2,3]
。
图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10
个单位。
示例:
输入: [2,1,5,6,2,3] 输出: 10
方法一:
考虑回文串的一种从中间往两头找的算法,首先遍历,对于每一个柱状图来说,向左右搜索,直到找到比他小的柱状图(假设为l和r),这样算出该柱状图的对应的矩形的面积为(r-l+1)*heights[cur]
。复杂度:O(n*n)。
class Solution { public: int largestRectangleArea(vector<int>& heights) { if(heights.empty()) return 0; int n = heights.size(); int res = 0; for(int i=0;i<n;++i) { int j = i-1, k=i+1; while(j>=0 && heights[j]>=heights[i]) j--; while(k<n && heights[k]>=heights[i]) k++; res = max(res, heights[i]*(k-j-1)); } return res; } };
方法二:
简单来说,维护一个单调栈,如果当前元素大于栈顶元素,则把当前元素的坐标入栈,否则,弹出栈顶元素直到当前元素可入栈。
在栈顶元素弹出时,把弹出的元素当成最短木板,计算能组成的最大面积是多少。时间复杂度:O(n)。
为什么栈中保存的是下标而不是数组本身的值? 因为,维护的是递增序列,计算面积总是以短板为高度,中间部分也是取短板的高度,宽度计算在内。
可以按照下面的理解,以栈中保存值本身而不是下标为例:
比如2,1,5,6,2,3
(1)2进栈。s={2}, result = 0
(2)1比2小,不满足升序条件,因此将2弹出,并记录当前结果为2*1=2。将2替换为1重新进栈。s={1,1}, result = 2
(3)5比1大,满足升序条件,进栈。s={1,1,5},result = 2
(4)6比5大,满足升序条件,进栈。s={1,1,5,6},result = 2
(5)2比6小,不满足升序条件,因此将6弹出,并记录当前结果为6*1=6。s={1,1,5},result = 6,2比5小,不满足升序条件,因此将5弹出,并记录当前结果为5*2=10(因为已经弹出的5,6是升序的)。s={1,1},result = 102比1大,将弹出的5,6替换为2重新进栈。s={1,1,2,2,2},result = 10
(6)3比2大,满足升序条件,进栈。s={1,1,2,2,2,3},result = 10,栈构建完成,满足升序条件,因此按照升序处理办法得到上述的max(height[i]*(size-i))=max{3*1, 2*2, 2*3, 2*4, 1*5, 1*6}=8<10
综上所述,result=10
class Solution { public: int largestRectangleArea(vector<int>& heights) { int res = 0; if(heights.empty()) return res; stack<int> st; heights.push_back(0); for(int i=0;i<heights.size();++i) { while (!st.empty() && heights[st.top()] >= heights[i]) { int cur = st.top(); st.pop(); int dis = st.empty() ? i : (i - st.top() - 1); res = max(res, heights[cur] * dis); } st.push(i); } return res; } };
2、85. 最大矩形
给定一个仅包含 0 和 1 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。
示例:
输入: [ ["1","0","1","0","0"], ["1","0","1","1","1"], ["1","1","1","1","1"], ["1","0","0","1","0"] ] 输出: 6
此题是上面那道的的扩展,这道题的二维矩阵每一层向上都可以看做一个直方图,输入矩阵有多少行,就可以形成多少个直方图,对每个直方图都调用 “直方图中最大的矩阵”中的方法,就可以得到最大的矩形面积。那么这道题唯一要做的就是将每一层构成直方图,由于题目限定了输入矩阵的字符只有 '0' 和 '1' 两种,所以处理起来也相对简单。
方法是,对于每一个点,如果是‘0’,则赋0,如果是 ‘1’,就赋 之前的height值加上1。(相当于用“1”来组成直方图)
class Solution { public: int maximalRectangle(vector<vector<char>>& matrix) { if(matrix.empty()) return 0; int m = matrix.size(); int n = matrix[0].size(); vector<int> height(n); int res = 0; for(int i=0;i<m;++i) { for(int j=0;j<n;++j) { height[j] = (matrix[i][j] == '0'? 0 : (height[j]+1));//如果先加的一行此处位置为0,则就是0,否则在原来的基础上加1 } res = max(res,helper(height) ); } return res; } int helper(vector<int>& heights) { int res = 0; if(heights.empty()) return res; stack<int> st; heights.push_back(0); for(int i=0;i<heights.size();++i) { while (!st.empty() && heights[st.top()] >= heights[i]) { int cur = st.top(); st.pop(); int dis = st.empty() ? i : (i - st.top() - 1); res = max(res, heights[cur] * dis); } st.push(i); } return res; } };