题目链接:Maximal Rectangle
Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing all ones and return its area.
这道题的要求是在0-1矩阵中找出面积最大的全1矩阵。
如果下面的粗略分析看不懂的话,关于这个问题非常具体详细的解析可以参考:https://www.cnblogs.com/lupx/archive/2015/10/20/leetcode-85.html
1. 基于Largest Rectangle in Histogram
假设把矩阵沿着某一行分开,然后把分开的行作为底面,将自底面往上的矩阵看成一个直方图(histogram)。直方图的中每个项的高度就是从底面行开始往上1的数量。根据Largest Rectangle in Histogram就可以求出当前行作为矩阵下边缘的一个最大矩阵。接下来如果对每一行都做一次Largest Rectangle in Histogram,从其中选出最大的矩阵,那么它就是整个矩阵中面积最大的子矩阵。
如何计算某一行为底面时直方图的高度呢?如果重新计算,那么每次需要的计算数量就是当前行数乘以列数。然而会发现一些动态规划的踪迹,如果知道上一行直方图的高度,就只需要看新加进来的行(底面)上对应的列元素是不是0,如果是,则高度是0,否则则是上一行直方图的高度加1。利用历史信息,就可以在线行时间内完成对高度的更新。由于Largest Rectangle in Histogram的算法复杂度是O(n)。所以完成对一行为底边的矩阵求解复杂度是O(n+n)=O(n)。接下来对每一行都做一次,那么算法总时间复杂度是O(m*n)。
时间复杂度:O(mn)
空间复杂度:O(n)
1 class Solution
2 {
3 public:
4 int maximalRectangle(vector<vector<char> > &matrix)
5 {
6 if(matrix.size() == 0 || matrix[0].size() == 0)
7 return 0;
8
9 int res = 0;
10 vector<int> height(matrix[0].size(), 0);
11 for(int i = 0; i < matrix.size(); ++ i)
12 {
13 for(int j = 0; j < matrix[0].size(); ++ j)
14 height[j] = matrix[i][j] == '0' ? 0 : height[j] + 1;
15 res = max(res, largestRectangleArea(height));
16 }
17 return res;
18 }
19 private:
20 int largestRectangleArea(vector<int> &height)
21 {
22 int res = 0;
23 stack<int> si;
24 height.push_back(0);
25 for(int i = 0; i < height.size(); ++ i)
26 {
27 while(!si.empty() && height[si.top()] >= height[i])
28 {
29 int h = height[si.top()];
30 si.pop();
31
32 int s = h * (si.empty() ? i : (i - si.top() - 1));
33 res = max(res, s);
34 }
35 si.push(i);
36 }
37 return res;
38 }
39 };
2. 动态规划
思路同样是从第一行开始一行一行地处理,使[i, j]处最大子矩阵的面积是(right(i, j)-left(i, j))*height(i, j)。其中height统计当前位置及往上'1'的数量;left和right是高度是当前点的height值得左右边界,即是以当前点为中心,以height为高度向两边扩散的左右边界。
递推公式如下:
left(i, j) = max(left(i-1, j), cur_left);
right(i, j) = min(right(i-1, j), cur_right);
height(i, j) = height(i-1, j) + 1, if matrix[i][j]=='1';
height(i, j) = 0, if matrix[i][j]=='0'.
其中cur_left和cur_right的值由当前行决定,如果当前位置是'1',则cur_left和cur_right都不变;如果当前位置是'0',则cur_left就为当前位置的右侧,cur_right就为当前位置(因为左闭右开)。
详见这里。
时间复杂度:O(mn)
空间复杂度:O(n)
1 class Solution
2 {
3 public:
4 int maximalRectangle(vector<vector<char> > &matrix)
5 {
6 if(matrix.size() == 0 || matrix[0].size() == 0)
7 return 0;
8
9 int m = matrix.size(), n = matrix[0].size(), res = 0;
10 vector<int> left(n, 0), right(n, n), height(n, 0);
11
12 for(int i = 0; i < m; ++ i)
13 {
14 int cur_left = 0, cur_right = n;
15
16 for(int j = 0; j < n; ++ j)
17 height[j] = matrix[i][j] == '1' ? height[j] + 1 : 0;
18
19 for(int j = 0; j < n; ++ j)
20 {
21 left[j] = matrix[i][j] == '1' ? max(left[j], cur_left) : 0;
22 cur_left = matrix[i][j] == '1' ? cur_left : j + 1;
23 }
24
25 for(int j = n - 1; j >= 0; -- j)
26 {
27 right[j] = matrix[i][j] == '1' ? min(right[j], cur_right) : n;
28 cur_right = matrix[i][j] == '1' ? cur_right : j;
29 }
30
31 for(int j = 0; j < n; ++ j)
32 res = max(res, (right[j] - left[j]) * height[j]);
33 }
34
35 return res;
36 }
37 };