85. Maximal Rectangle
Given a rows x cols binary matrix filled with 0’s and 1’s, find the largest rectangle containing only 1’s and return its area.
Ex
Example 1:
Input: matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
Output: 6
Explanation: The maximal rectangle is shown in the above picture.
Example 2:
Input: matrix = [["0"]]
Output: 0
Example 3:
Input: matrix = [["1"]]
Output: 1
Constraints:
rows == matrix.length
cols == matrix[i].length
1 <= row, cols <= 200
matrix[i][j] is ‘0’ or ‘1’.
V1-暴力算法
思路
我们直接遍历整个 m[i][j],然后以当前位置作为出发点,向下向右遍历。
m[i][j] x x x
x
x
x
1) 如果 m[i][j] == 0
,直接跳过。
2) 如果不是,则向右向下,一直到矩阵的最右下角,构成一个矩形。判断这个矩形是不是全部是1
3)如果全部是1,则更新计算 maxArea 的值。
java
public class T085_MaximalRectangle {
private boolean isAllOnes(char[][] matrix,
int xStart,
int xEnd,
int yStart,
int yEnd) {
for(int x = xStart; x <= xEnd; x++) {
for(int y = yStart; y <= yEnd; y++) {
if(matrix[x][y] == '0') {
return false;
}
}
}
return true;
}
/**
* 最大的长方形
* @param matrix 矩阵
* @return
*/
public int maximalRectangle(char[][] matrix) {
int maxArea = 0;
// 行
int rowSize = matrix.length;
// 列
int columnSize = matrix[0].length;
// 直接遍历整个矩阵
for(int i = 0; i < rowSize; i++) {
for(int j = 0; j < columnSize; j++) {
// 当前位置为0,则跳过
if(matrix[i][j] == '0') {
continue;
}
// 从当前位置开始,向右向下遍历。
for(int x = i; x < rowSize; x++) {
for(int y = j; y < columnSize; y++) {
// 跳过为0的元素
if(matrix[x][y] == '0') {
continue;
}
// 判断从 横介于[i,x],纵介于[j,y]的矩阵。
// 如果这个矩阵都是1,则计算更新对应的 area。
if(isAllOnes(matrix, i, x, j, y)) {
int area = (x-i+1) * (y-j + 1) ;
maxArea = Math.max(maxArea, area);
}
}
}
}
}
return maxArea;
}
}
当然,这个时间复杂度为 O(N^6),在 70/74 的时候直接 GG。
V2-初步优化
思路
我们在判断是否都是 1 的时候,显然会不断的重复这个过程。
如果能用 cache 记录一下这个过程,可以复用。
boolean[][] isAllOneCache = new boolean[][];
更新逻辑:
boolean isOneFlag = matrix[p][q]=='1';
// 左边的结果 & 当前
if(p>i) dp[p][q] = isOneFlag & dp[p-1][q];
// 上边的结果 & 当前
if(q>j) dp[p][q] = isOneFlag & dp[p][q-1];
实现
public class T085_MaximalRectangleV2 {
/**
* 最大的长方形
*
* 优化思路:缓存对于是否全部为 1 的判断
* @param matrix 矩阵
* @return
*/
public int maximalRectangle(char[][] matrix) {
int maxArea = 0;
// 行
int rowSize = matrix.length;
// 列
int columnSize = matrix[0].length;
// 直接遍历整个矩阵
for(int i = 0; i < rowSize; i++) {
for(int j = 0; j < columnSize; j++) {
// 当前位置为0,则跳过
if(matrix[i][j] == '0') {
continue;
}
// 是否都为1的缓存
boolean[][] allOneDp = new boolean[rowSize][columnSize];
// 从当前位置开始,向右向下遍历。
for(int x = i; x < rowSize; x++) {
for(int y = j; y < columnSize; y++) {
allOneDp[x][y] = matrix[x][y] == '1';
// 复用以前的
if(x > i) {
allOneDp[x][y] = allOneDp[x][y] & allOneDp[x-1][y];
}
if(y > j) {
allOneDp[x][y] = allOneDp[x][y] & allOneDp[x][y-1];
}
// 如果满足,则计算更新
if(allOneDp[x][y]) {
maxArea = Math.max(maxArea, (x-i+1)*(y-j+1));
}
}
}
}
}
return maxArea;
}
}
这个 TC 为 O(N^4),不过还是会在 70/74 超时。
V3-O(n^2)
思路
我们可以在 2D 矩阵的每一行中应用直方图中的最大值。 我们需要的是为每一行维护一个 int 数组,它代表直方图的高度。
请先参考https://leetcode.com/problems/largest-rectangle-in-histogram/。
假设有一个二维矩阵
1 1 0 1 0 1
0 1 0 0 1 1
1 1 1 1 0 1
1 1 1 1 0 1
首先将高度数组初始化为 1 1 0 1 0 1,这只是第一行的副本。 然后我们可以很容易地计算出最大面积为2。
然后更新数组。 我们扫描第二行,当matrix[1][i]为0时,设置height[i]为0; else height[i] += 1,这意味着高度增加了 1。所以高度数组再次变为 0 2 0 0 1 2。现在最大面积也是 2。
应用相同的方法,直到我们扫描整个矩阵。 最后一个高度数组是 2 4 2 2 0 4,所以最大面积为 2 * 4 = 8。
那么我们扫描整个矩阵的原因是最大值可能出现在高度的任何一行。
实现
public int maximalRectangle(char[][] matrix) {
if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
return 0;
}
int[] height = new int[matrix[0].length];
for (int i = 0; i < matrix[0].length; i++) {
if (matrix[0][i] == '1') {
height[i] = 1;
}
}
int result = largestInLine(height);
for (int i = 1; i < matrix.length; i++) {
resetHeight(matrix, height, i);
result = Math.max(result, largestInLine(height));
}
return result;
}
private void resetHeight(char[][] matrix, int[] height, int idx) {
for (int i = 0; i < matrix[0].length; i++) {
if (matrix[idx][i] == '1') {
height[i] += 1;
} else {
height[i] = 0;
}
}
}
public int largestInLine(int[] height) {
if (height == null || height.length == 0) {
return 0;
}
int len = height.length;
Stack<Integer> s = new Stack<>();
int maxArea = 0;
for (int i = 0; i <= len; i++) {
int h = (i == len ? 0 : height[i]);
if (s.isEmpty() || h >= height[s.peek()]) {
s.push(i);
} else {
int tp = s.pop();
maxArea = Math.max(maxArea, height[tp] * (s.isEmpty() ? i : i - 1 - s.peek()));
i--;
}
}
return maxArea;
}
参考资料
https://leetcode.com/problems/maximal-rectangle/solutions/29055/my-java-solution-based-on-maximum-rectangle-in-histogram-with-explanation/
https://leetcode.com/problems/maximal-rectangle/solutions/29054/share-my-dp-solution/
https://leetcode.com/problems/maximal-rectangle/solutions/29094/evolve-from-brute-force-to-optimal/
https://leetcode.com/problems/maximal-rectangle/