题目
给定一个整形矩阵map,其中的值只有0和1,求其中全是1的所有矩形区域中,最大的矩形区域中1的数量,例如:1 1 1 0,其中最大的矩形区域有3个1,返回3.
再如:
1 0 1 1
1 1 1 1
1 1 1 0
其中,最大的矩形区域有6个1,返回6
解答
如果矩阵大小为O(N*M),可以做到时间复杂度为O(N*M).
- 矩阵切割:矩阵行数为N,对每行进行切割,以当前行为底,统计每列的1的数量,使用数组height来存储,比如题目中的3*4的矩阵的举例,以第一行做切割,height={1,0,1,1},以第二行做切割,height={2,1,2,2},以第三行做切割,height={3,2,3,0}
- 求解最大矩阵:对每行的height分别去求最大矩阵,最后选出一个最大值即可。
- 求解最大矩阵有个问题,为了好理解再次举例,如果height={3,2,3,0},则最大矩阵面积为2*3=6
- 接下来我们把height数组看作直方图,每一个数字代表一个柱子,我们实际就是在求每个柱子能扩展多大的矩形,也就是说,需要找到柱子左右两侧离他最近且比他小的柱子的位置,这样子做就可以通过左右两个边界的差*当前柱子的高=当前柱子可扩展的矩形的面积
- 需要找到柱子左右两侧离他最近且比他小的柱子的位置,就是我们之前做过的单调栈问题,读者是不是很熟悉,可以看我上一篇文章对于单调栈问题的讲解。
- 此处建立在读者理解了单调栈结构的基础上,进行进一步对题的分析:
1>遍历height数组
2>栈为空或者当前height[i]大于栈顶元素直接入栈
3>若栈不为空,并且当前height[i]小于栈顶元素 j(假设为j,因为栈里存放的是数组索引值,这里比较的是height[i]<=height[j]),则需要弹出栈顶元素,则对于弹出的元素j来说左边位置离j最近并且小于arr[j]的值为当前栈顶(记为k)对应的值,右边位置则是i;
4>同理来说,对于当前弹出栈顶j,如果height[j]>height[i],则j所在的柱子向右扩展最多到i-1,因为height[i]已经比height[j]小了;如果height[j]==height[i],则j所在的柱子向右扩展至少到i-1,因为height[i]只是等于height[j];
5>对于j所在的柱子,向左最远可以扩展到k+1(k为当前栈顶元素,因为height[k]已经比height[j]小了)
6>综上,j位置能的最大矩形为: [(i-1)-(k+1)+1]*height[j]=(i-k-1)*height[j]
7> 遍历完数组后,若栈不为空,则对于栈中的所有元素,它们的右边位置最近并且小于他们的值都不存在,我们赋予i为height.length;j位置能的最大矩形公式变为为: [(height.length-1)-(k+1)+1]*height[j]=(height.length-k-1)*height[j]
疑惑解答:对于步骤4>,若height[j]==height[i],则向右至少为i-1,那么求出来的矩形面积不是偏小嘛?其实确实存在,但是不用担心因为我们此刻针对的是j来求解,当i进栈后,再次弹栈时,就会求解出正确的,因为i柱子和j柱子扩展出来的最大矩形是同一个。
源码
public int maxRecSize(int[][] map){
if(map==null||map.length==0||map[0].length==0){
return 0;
}
int maxArea=0;
int[] height=new int[map[0].length];
for(int i=0;i<map.length;i++){
for(int j=0;j<map[0].length;j++){
height[j]=map[i][j]==0?0:height[j]+1;
}
maxArea=Math.max(maxRecFromBottom(height),maxArea);
}
return maxArea;
}
public int maxRecFromBottom(int[] height){
if(height==null||height.length==0){
return 0;
}
int maxArea=0;
Stack<Integer> stack=new Stack<Integer>();
for(int i=0;i<height.length;i++){
while(!stack.isEmpty()&&height[i]<=height[stack.peek()]){
int j=stack.pop();
int k=stack.isEmpty()?-1:stack.peek();
int curArea=(i-k-1)*height[j];
maxArea=Math.max(maxArea,curArea);
}
stack.push(i);
}
while(!stack.isEmpty()){
int j=stack.pop();
int k=stack.isEmpty()?-1:stack.peek();
int curArea=(height.length-k-1)*height[j];
maxArea=Math.max(maxArea,curArea);
}
return maxArea;
}
此题偏难,建议小伙伴看之前先看一下前面的关于单调栈的文章,有问题欢迎随时交流讨论。