​welcome to my blog​

LeetCode Top 100 Liked Questions 85. Maximal Rectangle (Java版; Hard)

题目描述

Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and return its area.

Example:

Input:
[
["1","0","1","0","0"],
["1","0","1","1","1"],
["1","1","1","1","1"],
["1","0","0","1","0"]
]
Output: 6
class Solution {
public int maximalRectangle(char[][] matrix) {
if(matrix.length==0 || matrix[0].length==0){
return 0;
}
int n = matrix.length, m = matrix[0].length;
int[] height = new int[m];
int res=0, cur=0;
Stack<Integer> s = new Stack<>();
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
height[j] = matrix[i][j]=='1'? height[j]+1 : 0;
}
cur = core(height, s);
res = Math.max(res, cur);
}
return res;
}

private int core(int[] height, Stack<Integer> s){
s.clear();
s.push(-1);
int res=0, cur;
//遍历阶段; 每个元素进栈一次 (可能会出栈一次, 或者在清算阶段时出栈一次)
for(int i=0; i<height.length; i++){
while(s.peek()!=-1 && height[s.peek()] > height[i]){
int j = s.pop();
res = Math.max(res, (i-s.peek()-1)*height[j]);
}
s.push(i);
}
//清算阶段: 数组中的最后一个元素一定在栈顶
while(s.peek()!=-1){
int j = s.pop();
res = Math.max(res, (height.length - 1 -s.peek())*height[j]);
}
return res;
}
}

第一次做, 使用动态规划, 一层一层处理; 结合最下面的图去理解递推公式; 速度最快, 但是不便于理解, 还要是牢牢掌握单调栈的解法

/*
动态规划版本, 按层计算
对于当前层, 遍历每一列, 对于某一列, 找出该列左右两侧离该列最近且比该列小的柱子
*/
import java.util.Arrays;

class Solution {
public int maximalRectangle(char[][] matrix) {
if(matrix==null || matrix.length==0 || matrix[0].length==0)
return 0;
//
int rows = matrix.length;
int cols = matrix[0].length;
//对于某一层的某一列, left[j]表示j左边离j最近的并且比matrix[][j]小的柱子
int[] left = new int[cols];
Arrays.fill(left, -1);
//对于某一层的某一列, right[j]表示j右边离j最近的并且比matrix[][j]小的柱子
int[] right = new int[cols];
Arrays.fill(right, cols);
//以某一层的某个点为底, 往上有多少个连续的1, 包括底在内
int[] heights = new int[cols];
//newest0表示某一列中, 最靠近j的0的列索引
int res=0, curr, newest0=-1;
for(int i=0; i<rows; i++){
//以当前层的各个点为底, 往上有多少个连续的1
for(int j=0; j<cols; j++){
heights[j] = matrix[i][j]=='1' ? heights[j]+1 : 0;
}
//更新left, 找出matrix[][j]左边距离j最近的并且比matrix[][j]小的柱子
newest0 = -1;
for(int j=0; j<cols; j++){
//理解下面的语句要结合图, 否则太抽象了
//当前位置是1, 对应的left[j]要么不变, 要么更靠近j了
//更靠近j是因为在靠近j的位置出现了0
if(matrix[i][j] == '1'){
left[j] = Math.max(left[j], newest0);
}
//当前位置是0, 不存在比该位置对应值更小的柱子了
else{
left[j] = -1;//这里赋值为-1相当于初始化, 让下一行使用; 不能赋成大于等于0的值
newest0 = j;
}
}
//更新right
newest0 = cols;
for(int j=cols-1; j>=0; j--){
if(matrix[i][j]=='1'){
right[j] = Math.min(right[j], newest0);
}
//当前位置为0, 不存在比该位置对应值更小的柱子了
else{
right[j] = cols;//这里赋成cols相当于初始化, 让下一行使用; 不能赋成小于cols的值
newest0 = j;
}
}
//计算面积
for(int j=0; j<cols; j++){
curr = (right[j] - left[j] - 1) * heights[j];
res = Math.max(res, curr);
}
}
return res;
}
}

第一次做, 多次使用单调栈结构; 栈底到栈顶递增; 注意计算面积时,为什么宽度不能用index求,而必须用s.peek()求; char与ascii与整数的细节, 看代码

/*
使用单调栈: 栈底到栈顶递增
和84题很像, 使用84单调栈的处理方式
*/
import java.util.Stack;

class Solution {
public int maximalRectangle(char[][] matrix) {
if(matrix==null || matrix.length==0 || matrix[0].length==0)
return 0;
//
int[] heights = new int[matrix[0].length];
int res = 0, curr;
Stack<Integer> s = new Stack<>();
for(int i=0; i<matrix.length; i++){
for(int j=0; j<matrix[0].length; j++){
//统计以matrix[i][j]往上有多少个连续的1, 包括matrix[i][j]自己
//ascii: 48是'0', 49是'1'
heights[j] = matrix[i][j]==49? heights[j]+1 : 0;
/*
这里也可以写成
heights[j] = matrix[i][j]=='1' ? heights[j]+1 : 0;
*/
}
curr = monotonousStack(heights, s);
res = Math.max(res, curr);
}
return res;
}
public int monotonousStack(int[] heights, Stack<Integer> s){
//由于矩阵的每一行用的都是同一个栈, 所以使用前先清空一下栈
s.clear();

//单调栈: 栈底到栈顶递增
//栈中存索引; 压入参考值-1
s.push(-1);
//遍历阶段
int res=0, curr, index;
for(int i=0; i<heights.length; i++){
while(s.peek()!=-1 && heights[s.peek()] > heights[i]){
//弹出栈顶元素
index = s.pop();
//计算面积
//计算宽度时必须用s.peek(),不能用index! 因为从index处也许能够向左扩展(和左神讲的思路一致); 想不明白用用[3,1,3,2,2]进行debug
curr = heights[index] * (i - s.peek() - 1);
//
res = Math.max(res, curr);
}
s.push(i);
}
//清算阶段
while(s.peek()!=-1){
//heights[index]是{index,index+1,...,heights.length-1}范围上值最小的
index = s.pop();
//计算面积
curr = heights[index] * (heights.length-1 - s.peek());
//
res = Math.max(res, curr);
}
return res;
}
}

按层来看, 这道题就是84题的加强版

84题图

LeetCode Top 100 Liked Questions 85. Maximal Rectangle (Java版; Hard)_LeetCode

85题图, ​​图片来源​

LeetCode Top 100 Liked Questions 85. Maximal Rectangle (Java版; Hard)_Stack_02