题目描述
给定一个整型矩阵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) ?
1 : 矩阵的行数为 N, 以每一行做切割, 统计以当前行作为低的情况下,每个位置往上1的数量,使用高度数组height来表示。
2:对每一次切割, 都利用更新后的height数组来求出以每一行为底的情况下,最大的矩形是什么,那么这么多次切割中,最大的那个矩形就是我们要的。
对于步骤2,如何快速的实现,时间复杂度为O(M) ? 用栈
对于height数组 , 将其理解为一个直方图,步骤2的实质是在一个大的直方图中求最大矩形的面积。对每一根柱子,找到柱子左边第一个比它小的柱子的位置和柱子右边第一个比它大的柱子的位置 , 就可以轻松地求出以该根柱子扩展出去的最大矩形。
栈的使用:
从左到右遍历数组height, 遍历到i元素:
关键逻辑:
1: 只有当前i位置的值大于栈顶位置所代表的值,元素i压入
2:如果当前i位置的值小于或等于当前栈顶位置所代表的值,则把栈中存的位置不断弹出,直到某一个栈顶所代表的值小于当前位置的值,再把i压入
在弹出时:
1: 假设当前弹出的栈顶位置记为j,弹出栈顶之后,新的栈顶记为k,
对于柱子j,向右最远能扩到 i- 1 , 如果所有元素压入栈后,栈不为空,则向右最远扩到height.length,
对于柱子j,向左最远能扩到k + 1 , 如果此时栈为空,向左最远扩到0,说明略
代码
package TestNode.StackAndQueue;
import java.util.Stack;
public class MaxSubRecSize {
public static 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;
}
private static int maxRecFromBottom(int[] height){
if (height==null || height.length==0){
return 0;
}
int maxArea = 0;
Stack<Integer> stack = new Stack<>();
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;
}
public static void main(String[] args) {
int[][] map = new int[][]{
{1,0,1,1},
{1,1,1,1},
{1,1,1,0}};
System.out.println(maxRecSize(map));
}
}