LeetCode/最大矩形
作者:互联网
给定一个仅包含 0 和 1 、大小为 rows x cols 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。
1. 暴力求解
该问选择合适的暴力方式也有一定的难度,既要遍历所有可能的矩形,又要尽可能减少重复运算
可以考察以每个点为右下角的最大矩阵,即在二重循环遍历中,计算每个点所有高度的面积
这样我们就能实现通过递推的方式事先算出需要用到的值,减少重复运算
left[i][j] 为矩阵第 i 行第 j 列元素的左边连续 1 的数量,这个信息可以通过递推事先计算出来
其本质是将这部分区域矩形转换成了柱状图,再求柱状图的最大矩形面积
//要使用暴力求解的话,遍历每一个点,要能对所有矩阵进行考察
//可以考察以该点为右下角的最大矩阵,即在二重循环遍历中,计算每个点所有高度的面积
//时间复杂度为O(m2n)
//left[i][j] 为矩阵第 i 行第 j 列元素的左边连续 1 的数量,也就是矩形的宽度
class Solution {
public:
int maximalRectangle(vector<vector<char>>& matrix) {
int m = matrix.size();//记录行数
if (m == 0) return 0;
int n = matrix[0].size();//记录列数
vector<vector<int>> left(m, vector<int>(n, 0));//动态规划辅助计算
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (matrix[i][j] == '1')
//计算第 i 行第 j 列元素的左边连续 1 的数量,方便矩形面积计算
//本质上就是将其转换成以该点为右下边界所在区域的柱状图模型
left[i][j] = (j == 0 ? 0: left[i][j - 1]) + 1;
}
}
int ret = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
//跳过为0的矩阵减少运算
if (matrix[i][j] == '0') continue;
int width = left[i][j];//记录当前宽度
int area = width;
for (int k = i - 1; k >= 0; k--) {//遍历所有高度
width = min(width, left[k][j]);//更新宽度
area = max(area, (i - k + 1) * width);//计算并更新该点最大面积
}
ret = max(ret, area);//更新所有点对应最大面积
}
}
return ret;
}
};
2. 单调栈
既然方法一的实质是转换成柱状图再对每个柱状图遍历,那可以结合之前求柱状图中最大矩形的优化方式来进一步优化
单调栈优化
class Solution {
public:
int maximalRectangle(vector<vector<char>>& matrix) {
int m = matrix.size();//记录行数
if (m == 0) return 0;
int n = matrix[0].size();//记录列数
vector<vector<int>> up(m, vector<int>(n, 0));//动态规划辅助计算
for (int i = 0; i < m; i++)
for (int j = 0; j < n; j++)
if (matrix[i][j] == '1')
up[i][j] = (i == 0 ? 0: up[i-1][j]) + 1;//转换成柱状图
int res = INT_MIN;
for(int i = 0; i < m; i++)
res = max(res,largestRectangleArea(up[i]));//从上往下计算每一层作为底层的柱状图
return res;
}
int largestRectangleArea(vector<int>& heights) {
int n = heights.size();
vector<int> left(n), right(n);//记录每个柱子左右,满足条件的最远下标
//条件指得是第一个小于柱子高度的位置
stack<int> mono_stack;//单调栈
for (int i = 0; i < n; ++i) {//从左往右遍历找每个元素左侧满足条件最远位置
while (!mono_stack.empty() && heights[mono_stack.top()] >= heights[i]) {
mono_stack.pop();//如果左边这个元素大于当下节点,那他是包容右边的
//提供了当下节点构成左边矩形区域一部分
//所以可以不用看了,直接去掉,因为它被当下节点截断了,不会再影响后面元素
//同时要露出我们的左边界
}
//如果满足增序关系,那左边界就是他前一个元素
left[i] = (mono_stack.empty() ? -1 : mono_stack.top());
//当下节点的左边界,即是左边第一个小于它的元素,因为大的元素全部去掉了
mono_stack.push(i);//当下节点入栈,给后面元素继续判断
}
mono_stack = stack<int>();//单调栈
for (int i = n - 1; i >= 0; --i) {
while (!mono_stack.empty() && heights[mono_stack.top()] >= heights[i]) {
mono_stack.pop();
}
right[i] = (mono_stack.empty() ? n : mono_stack.top());
mono_stack.push(i);
}
int ans = 0;
for (int i = 0; i < n; ++i) {
ans = max(ans, (right[i] - left[i] - 1) * heights[i]);
}
return ans;
}
};
标签:最大,int,mono,++,LeetCode,matrix,矩形,stack,left 来源: https://www.cnblogs.com/929code/p/16320587.html