每日一题 为了工作 2020 0311 第九题

* 单调栈的相关应用

/**
* 问题:求最大子矩阵的大小
* 给定一个整形矩阵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+"\t");
            }
            System.out.println();
        }
        System.out.println();
    }
}

 

posted @ 2020-03-11 13:52  雪瞳  阅读(141)  评论(0编辑  收藏  举报