* 单调栈的相关应用
/**
* 问题:求最大子矩阵的大小
* 给定一个整形矩阵map,其中的值只有0和1两种,求其中全是1的所有矩形区域中,最大的矩形区域为1的数量。
* 例如:
* 1 1 1 0
* 其中,最大的矩形区域有三个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来表示。
*
* 例如:
* map = 1 0 1 1
* 1 1 1 1
* 1 1 1 0
*
* 1.以第一行做切割后,height={1,0,1,1},height[j]表示在目前的底(第一行)的j位置往上(包括j位置),有多少个连续的1。
*
* 2.以第二行做切割后,height={2,1,2,2},height[j]表示在目前的底(第二行)的j位置往上(包括j位置),有多少个连续的1。
* 注意到从第一行到第二行,height数组的更新是十分方便的,即为height[j]=(map[i][j]==0?0:height[j]+1)。
*
* 3.以第三行做切割后,height={3,2,3,0},height[j]表示在目前的底(第一行)的j位置往上(包括j位置),有多少个连续的1。
*
* 2.对于每一次切割,都利用更新后的height数组来求出以当前行为底的情况下,最大的矩形是什么。那么这么多次切割中,最大的那个矩形就
* 是我们要的答案。
*
* 整个 过程中就是如下代码中的maxRecSize方法。步骤2的实现是如下的maxRecFromBottom方法。
*
* 下面重点介绍一下步骤2是如何实现的,这也是这道题的最为重要的部分,如果height数组的长度为M,那么求解步骤2的过程可以做到时间
* 复杂度为O(M)。
*
* 对于height数组,我们可以理解为一个直方图,比如{3,2,3,0},相当于横轴为1、纵轴为height[j]高度的矩形拼成的图形。所以我们
* 可以认为步骤2的实质就是在一个大的直方图中求出最大矩形的面积。如果我们能够求出以每一根柱子扩展出去的最大矩形,那么其中的最大的矩
* 形就是我们想找的。
*
* 例入:
* 1.以第一根高度为3的柱子向左边无法扩展,它的右边是2,比3小,所以向右也无法扩展,则以第一根柱子为高度的矩形面积就是3*1=3。
* 2.以第二根高度为2的柱子向左边可以扩展一个距离,因为它的左边是3,比2大。同理,右边也可以扩展一个柱子,所以以第二根柱子为
* 高度的矩形面积就是2*3=6。
* 3.以第三根高度为3的柱子向左边无法扩展,它的左边是2,比3小,同时向右也无法扩展,则以第三根柱子为高度的矩形面积就是3*1=3。
* 4.以第四根高度为0的柱子向左边无法扩展,同时向右也无法扩展,则以第四根柱子为高度的矩形面积就是0*1=0。
*
* 所以:
* 在当前的height数组为{3,2,3,0}拼成的直方图内,最大的矩形面积为6.
*
* 考察每一根柱子最大能够扩多大,就是找到柱子左边离他最近且最小的柱子位置在哪里,以及柱子右边离他最近且最小的位置在哪里,这就需要用
* 到单调栈结构。
*
* 编程思路:
*
* 1.假设当前弹出的位置记为j,弹出j位置之后的新栈顶位置记为k,只需要找到j位置对应的柱子向左和向右最远可以扩多远。对于位置j来说,如果
* height[j]>height[i]。i为当前遍历的位置,j之所以被弹出是因为遇到了第一个比自己小的柱子,所以向右最远可扩展到i-1位置。
* 2.如果height[i]=height[j],那么i-1位置就不是向右扩展到最远的位置了,最起码j位置可以扩展到i位置,所以这样计算出来的矩形面积
* 会变小,但是在这种情况下i位置向左也必然可以扩展到j位置,也就是j位置扩出来的最大矩形和i位置扩出来的最大矩形是同一个,j位置的虽然
* 无法准确计算,但是主需要等i位置弹出计算即可。
* 3.对于j位置对应的柱子,向左最远可以扩展到当前新栈顶的位置k+1.因为k位置得值就是栈内比j位置小对应元素小且离j最近的位置。如果栈空则
* 记为-1,所以最大矩形的面积就是S=(i-k-1)*height[j]。
* 4.清算阶段则认为i=height.length==6,依次弹出计算即可。
*
* @author 雪瞳
*
*/
import java.util.Stack;
public class getMaxRecSize {
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(maxArea, maxRecFromBottom(height));
}
return maxArea;
}
public int maxRecFromBottom(int height[]) {
if(height ==null || height.length==0) {
return 0;
}else {
int maxArea = 0;//最大面积
Stack<Integer> stack = new Stack<>();
int indexElement = 0;//当前位置的高度
int popIndex = 0;//当前弹出位置元素
int leftIndex = 0;//当前位置向左最远可以走到哪里
int rightIndex = 0;//当前位置向右最远可以走到
int curArea = 0;//当前弹出位置扩出区域的面积值
for(int i=0;i<height.length;i++) {
indexElement = height[i];
while(!stack.isEmpty() && height[stack.peek()] >= indexElement) {
popIndex = stack.pop();
leftIndex = stack.isEmpty()?-1:stack.peek();
rightIndex = i;
curArea = (rightIndex-1-leftIndex)*height[popIndex];
maxArea = Math.max(maxArea, curArea);
}
stack.push(i);
}
//清算阶段
while(!stack.isEmpty()) {
popIndex = stack.pop();
leftIndex = stack.isEmpty()?-1:stack.peek();
rightIndex = height.length;
curArea = (rightIndex-1-leftIndex)*height[popIndex];
maxArea = Math.max(maxArea, curArea);
}
return maxArea;
}
}
}
import java.util.Random;
import java.util.Scanner;
public class testGetMaxRecSize {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Random rand = new Random();
getMaxRecSize getMax = new getMaxRecSize();
testGetMaxRecSize test = new testGetMaxRecSize();
System.out.println("请输入行数和列数,N代表行,M代表列!");
int N =sc.nextInt();
int M = sc.nextInt();
int array[][] = new int[N][M];
int maxArea = 0;
for(int i = 0;i<array.length;i++) {
for(int j=0;j<array[i].length;j++) {
array[i][j]=(rand.nextInt(2));
}
}
/*int maxArea= 0;
int array[][]=new int[][] {
{1,0,1,1},
{1,1,1,1},
{1,1,1,0}
};*/
test.show(array);
maxArea = getMax.maxRecSize(array);
System.out.println("最大的矩形面积为:"+maxArea);
}
public void show(int array[][]) {
for(int arr[]: array) {
for(int element: arr) {
System.out.print(element+" ");
}
System.out.println();
}
System.out.println();
}
}