84. 柱状图中最大的矩形
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
示例 1:
输入:heights = [2,1,5,6,2,3] 输出:10 解释:最大的矩形为图中红色区域,面积为 10
示例 2:
输入: heights = [2,4] 输出: 4
提示:
1 <= heights.length <=10(5)
0 <= heights[i] <= 10(4)
1.
思路就是以每一个位置的柱状图的高度作为矩形的高,然后看以这个高能够扩多长的底,计算这个情况下的面积.
问题被划分为几个小的部分,以每一个高度为高去计算.
2.
单调栈存储左小于,右小于.
老旧的单调栈写法是在元素出栈的时候维护数据.
但是发现这样特别浪费内存,有的时候还需要给栈开辟二维空间处理相同的数据.
这种做法实在是太低效了,并且在这道题目的提交结果居然超出了题目的内存.
3.
全新的版本,只用一个单独的栈就可以完成重复元素或者不重复元素.
首先我们需要确定我们的目的是什么,是填写信息每一个元素左小于,右小于的信息.
老旧版本是在出栈的时候维护信息,全新版本是在入栈的时候维护信息.
每一个元素入栈的时候,维护栈内单调性,然后维护入栈元素左小于或者右小于信息.
对于每一个入栈元素,栈内一定是单调性,此时一定可以找到左小于或者右小于.
老旧版本
class Solution {
public:vector<int> arr;int n;int ret;using p = pair<int, int>;vector<p> p_m;vector<vector<int>> st;void init() {n = arr.size();p_m.clear(), p_m.resize(n);}void solve() {int i = 0;while (1) {if (i >= n)break;if (st.empty())st.push_back({i});else {auto top = st.back();while (1) {if (arr[i] >= arr[top[0]])break;st.pop_back();for (auto& x : top) {p_m[x].second = i;p_m[x].first = st.empty() ? -1 : st.back().back();}if (st.empty())break;elsetop = st.back();}if (st.empty())st.push_back({i});else {if (arr[st.back()[0]] == arr[i])st.back().push_back(i);elsest.push_back({i});}}i++;}while(1){if(st.empty())break;auto top=st.back();st.pop_back();for(auto& x:top){p_m[x].second=n;p_m[x].first=st.empty()?-1:st.back().back();}}for(int i=0;i<n;i++){ret=max(ret,arr[i]*(p_m[i].second-p_m[i].first-1));}}int largestRectangleArea(vector<int>& _heights) {arr = _heights;init(), solve();return ret;}
};
全新版本
class Solution {
public:vector<int> arr;//下标-元素int n;//元素的个数int ret;//记录结果信息using p = pair<int, int>;vector<p> p_m;//记录每一个元素左小于,右小于的信息vector<int> st;//vector模拟栈void init() {n = arr.size();p_m.clear(), p_m.resize(n);}void solve() {//首先维护每一个元素的左小于的信息for (int i = 0; i < n; i++) {while (1) {//维护栈内单调性,严格的小到大if (st.empty())break;if (arr[st.back()] < arr[i])break;st.pop_back();//出栈,因为此时他们都不可能再作为某元素的左小于了.}//维护入栈元素的左小于信息p_m[i].first = st.empty() ? -1 : st.back();st.push_back(i);//入栈}st.clear();//栈清空//维护所有元素的右小于信息//此时必须是从后往前填写,因为对于j位置我需要找右小于,那么右边所有元素需要入栈并且栈保持单调for (int i = n - 1; i >= 0; i--) {while (1) {if (st.empty())break;if (arr[st.back()] < arr[i])break;st.pop_back();}p_m[i].second = st.empty() ? n : st.back();//维护右小于信息st.push_back(i);}for (int i = 0; i < n; i++) {ret = max(ret, arr[i] * (p_m[i].second - p_m[i].first - 1));}}int largestRectangleArea(vector<int>& _heights) {arr = _heights;init(), solve();return ret;}
};
85. 最大矩形
给定一个仅包含
0
和1
、大小为rows x cols
的二维二进制矩阵,找出只包含1
的最大矩形,并返回其面积。示例 1:
class Solution { public: vector<int> arr; //下标-元素 int n; //元素的个数 int ret; //记录结果信息 using p = pair<int, int>; vector<p> p_m; //记录每一个元素左小于,右小于的信息 vector<int> st; //vector模拟栈 void init() { n = arr.size(); p_m.clear(), p_m.resize(n); } void solve() { //首先维护每一个元素的左小于的信息 for (int i = 0; i < n; i++) { while (1) { //维护栈内单调性,严格的小到大 if (st.empty()) break; if (arr[st.back()] < arr[i]) break; st.pop_back(); //出栈,因为此时他们都不可能再作为某元素的左小于了. } //维护入栈元素的左小于信息 p_m[i].first = st.empty() ? -1 : st.back(); st.push_back(i); //入栈 } st.clear(); //栈清空 //维护所有元素的右小于信息 //此时必须是从后往前填写,因为对于j位置我需要找右小于,那么右边所有元素需要入栈并且栈保持单调 for (int i = n - 1; i >= 0; i--) { while (1) { if (st.empty()) break; if (arr[st.back()] < arr[i]) break; st.pop_back(); } p_m[i].second = st.empty() ? n : st.back(); //维护右小于信息 st.push_back(i); } for (int i = 0; i < n; i++) { ret = max(ret, arr[i] * (p_m[i].second - p_m[i].first - 1)); } } int largestRectangleArea(vector<int>& _heights) { arr = _heights; init(), solve(); return ret; } };
输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]] 输出:6 解释:最大矩形如上图所示。
示例 2:
输入:matrix = [["0"]] 输出:0
示例 3:
输入:matrix = [["1"]] 输出:1
提示:
rows == matrix.length
cols == matrix[0].length
1 <= row, cols <= 200
matrix[i][j]
为'0'
或'1'
1.
思路是每一行看作是柱状图的开始,那么题目完全转化为84. 柱状图中最大的矩形.
用dp记录每一个位置的柱状图高度.
每一行都计算一次,ret记录所有情况的最大值即可.
2.
入栈的时候维护信息.
一个循环维护左信息,一个循环维护右信息.
class Solution {
public:vector<vector<char>> arr;//arr表示矩形原始数据int row, col;//row和col表示矩形的行和列数int ret;//ret记录结果数据vector<vector<int>> dp;//dp记录所有位置的柱状图高度vector<vector<int>> p_min;//存储每一行的柱状图的左小于右小于信息vector<int> st;//模拟栈void init() {row = arr.size(), col = arr[0].size();dp.clear(), dp.resize(row, vector<int>(col));//计算dp值for (int j = 0; j < col; j++) {for (int i = 0; i < row; i++) {if (arr[i][j] == '1')dp[i][j] = 1 + (i - 1 >= 0 ? dp[i - 1][j] : 0);elsedp[i][j] = 0;}}p_min.clear(), p_min.resize(col, { 0, 0 });//用{}大括号初始化vector,长度小的时候很方便for (int i = 0; i < row; i++) {for (int j = 0; j < col; j++) {cout << dp[i][j] << " ";}cout << endl;}//看一下dp值有没有计算计算错误}void solve() {//正式开始解题for (int i = 0; i < row; i++) {//首先遍历每一行,表示每一个柱状图p_min.clear(), p_min.resize(col, { 0, 0 });st.clear();//初始化左小于右小于信息//初始化栈for (int j = 0; j < col; j++) {//先维护当前行的左小于信息while (1) {if (st.empty())break;if (dp[i][j] > dp[i][st.back()])break;st.pop_back();}p_min[j][0] = st.empty() ? -1 : st.back();//维护当前j位置左小于信息st.push_back(j);//入栈}st.clear();//栈清空//维护右小于信息for (int j = col - 1; j >= 0; j--) {while (1) {if (st.empty())break;if (dp[i][j] > dp[i][st.back()])break;st.pop_back();}p_min[j][1] = st.empty() ? col : st.back();st.push_back(j);}//此时完成当前行的左小于右小于信息维护//计算此时可能的矩形面积for (int j = 0; j < col; j++) {ret = max(ret, (p_min[j][1] - p_min[j][0] - 1) * dp[i][j]);}for (int j = 0; j < col; j++) {cout << p_min[j][0] << " " << p_min[j][1] << " ";}cout << endl;//打印看一下有没有问题}}int maximalRectangle(vector<vector<char>>& _matrix) {ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);arr = _matrix;init();solve();return ret;}
};
结尾
最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。
同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。
谢谢您的支持,期待与您在下一篇文章中再次相遇!