题目:
Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and return its area.
For example, given the following matrix:
1 0 1 0 0 1 0 1 1 1 1 1 1 1 1 1 0 0 1 0Return 6.
思路:
假设矩形的规模是m行n列,可以有如下三种基本思路:
1、暴力枚举法:一行一行的遍历数组,维护每个位置到顶部的高度,如果某位置高度大于0,就往回扫描到首列,同时维护一个当前最低的高度,这样就可以求出从这个位置到第一列的最大面积了。时间复杂度是O(m*n*n),当然通过转置可以优化到O(m*n*min(m, n)),但数量级不变。空间复杂度是O(n)。
2、动态规划法:由于在暴力枚举法中我们每碰到一个高度不为0的情况就往左搜索找到当前最低的高度然后更新面积,所以会导致重复计算。动态规划刚好可以通过记录原有信息来避免重复搜索。那么每一行我们需要记录什么呢?我们需要记录并维护的就是一行中每个位置高度的左右边界。
3、递增栈法:刚刚我们解决了“Largest Rectangle in Histogram”(Leetcode 85),这道题目可以看作是它的拓展,就是依次把每一行都当成一个柱形图的底,然后就可以直接采用Leetcode 85的思路了。
代码:
1、暴力枚举法(46m):
class Solution {
public:
int maximalRectangle(vector<vector<char>>& matrix) {
if(matrix.size() == 0) {
return 0;
}
int m = matrix.size(), n = matrix[0].size();
int ret = 0;
vector<int> heights(n, 0);
for(int i = 0; i < m; ++i) {
for(int j = 0; j < n; ++j) {
if(matrix[i][j] == '0') {
heights[j] = 0;
continue;
}
heights[j]++;
int min_height = heights[j];
for(int k = j; k >= 0; --k) {
min_height = min(heights[k], min_height);
ret = max(ret, (j - k + 1) *min_height);
}
}
}
return ret;
}
};
2、动态规划法(13ms):
class Solution {
public:
int maximalRectangle(vector<vector<char>>& matrix) {
if (matrix.size() == 0) {
return 0;
}
int m = matrix.size(), n = matrix[0].size();
int ret = 0;
vector<int> heights(n,0), lefts(n, 0), rights(n, n);
for (int i = 0; i < m; ++i) {
int cur_left = 0, cur_right = n;
for(int j = 0; j < n; ++j) {
heights[j] = matrix[i][j] == '0' ? 0 : heights[j] + 1;
if (heights[j] > 0) {
lefts[j] = max(lefts[j], cur_left);
}
else {
lefts[j] = 0;
cur_left = j + 1;
}
}
for(int j = n - 1; j >= 0; --j) {
if (heights[j] > 0) {
rights[j] = min(rights[j], cur_right);
}
else {
rights[j] = n;
cur_right = j;
}
}
for(int j = 0; j < n; ++j) {
ret = max(ret, (rights[j] - lefts[j]) * heights[j]);
}
}
return ret;
}
};
3、递增栈法(13ms):
class Solution {
public:
int maximalRectangle(vector<vector<char>>& matrix) {
if (matrix.size() == 0) {
return 0;
}
int m = matrix.size(), n = matrix[0].size();
int ret = 0;
vector<vector<int>> ones(m + 1, vector<int>(n, 0)); // store the number of ones above directly
for (int i = 1; i <= m; ++i) {
for (int j = 0; j < n; ++j) {
if (matrix[i - 1][j] == '1') {
ones[i][j] = ones[i-1][j] + 1;
}
else {
ones[i][j] = 0;
}
}
ret = max(ret, largestRectangleArea(ones[i]));
}
return ret;
}
private:
int largestRectangleArea(vector<int>& heights)
{
if(heights.size() == 0) {
return 0;
}
stack<int> s;
heights.push_back(0);
int sum = 0;
for(int i = 0; i < heights.size(); ++i)
{
if(s.empty() || heights[i] > heights[s.top()]) {
s.push(i);
}
else
{
int tmp = s.top();
s.pop();
// width is the key point to understand this algorithm
int width = s.empty() ? i : i - s.top() - 1;
sum = max(sum, heights[tmp] * width);
i--;
}
}
return sum;
}
};