【算法】【栈和队列模块】求一个[0,1]矩阵中最大的1矩阵面积

前言

当前所有算法都使用测试用例运行过,但是不保证100%的测试用例,如果存在问题务必联系批评指正~

在此感谢左大神的书让我对算法有了新的感悟认识!

问题介绍

原问题
给定二维数组,元素只有0和1,如
[1,0,1,1]
[1,1,1,1]
[1,1,1,0]
求最大的1矩阵面积,如上答案为6

解决方案

方案一:
1、非常重要的一个想法:一行一行的求以当前行为底时的最大矩阵大小,每一行应该怎么求?
2、每一行为底时可以看成是一个统计图中的柱状图,柱状图求最大面积怎么求?很显然在用1的想法,求一下以每一个柱子为基础求包含该柱子的最大面积
比如原问题例子,第二行柱状图为[2, 1,2, 2],包含柱子0的最大矩阵只能是2,右边的1阻断的拓展,因此问题再次变成了求每一个柱子左边第一个比自己小的值和右边第一个比自己小的值,然后就底长度就确定了,高度就是自己的高度,值就计算出来啦~至于怎么快速计算第一个比自己小的值,请看:

https://blog.csdn.net/qq_39760347/article/details/126437443

这样一来,再进行最大值的更新,就结束了~

方案二:
大同小异,比起方案一,方案二简化了操作,方案二选择弹出时再计算被弹出的pop为顶时的面积,这个时候pop的左边和右边界被一把找到。

代码编写

java语言版本

方案一

public class MaxSubMartrix {

    /**
     * 给一个矩阵,里面都是0,1,返回1组成的最大子矩阵
     * @param martrix
     * @return
     */
    public int maxRecSize(int[][] martrix){
        // 遍历每一行,得出当前最大值
        int maxInt = 0;
        // null 或者行列有一个是0的直接返回
        if(martrix == null || martrix.length == 0 || martrix[0].length == 0){
            return maxInt;
        }
        // 实时记录当前行的柱形高度
        int[] height = new int[martrix[0].length];
        // 遍历每一行
        for (int i = 0; i < martrix.length; i++){
            // 遍历每一列,每一列如果是0,则当前列柱初始化成0
            for (int j = 0; j < martrix[0].length; j++){
                height[j] = martrix[i][j] == 0? 0 : height[j] + 1;
            }
            // 加完之后获取当前层最大矩阵值
            maxInt = Math.max(getMaxForCurrentLine(height), maxInt);
        }
        return maxInt;
    }

    /**
     * 获取当前层的最大值
     * @param height
     * @return
     */
    private int getMaxForCurrentLine(int[] height){
        if(height == null || height.length == 0){
            return 0;
        }
        Stack<Integer> stack = new Stack<>();
        int max = 0;
        for (int i = 0; i < height.length; i++){
            // 栈保持栈顶到栈底 大-小,相等必须进去,否则会影响比该值大的值计算
            while (!stack.isEmpty() && height[i] <= height[stack.peek()]){
                Integer topIndex = stack.pop();
                int k = stack.isEmpty() ? -1 : stack.peek();
                // 这里在多个相等的情况下可能计算的不对,但是相等情况下当前计算的值也不会是最后的答案随意无关紧要
                max = Math.max(max, (i - k - 1) * height[topIndex]);
            }
            // 挖完之后把自己放进去,放的不是值,是角标
            stack.push(i);
        }
        // 结束之后如果stack还有值,说明右边没有比自己小的值了,就按照右边的边界减,则继续执行
        while (!stack.isEmpty()){
            int topIndex = stack.pop();
            int k = stack.isEmpty()? -1 : stack.peek();
            // 这里的i - k - 1 里面的i不会再动了,因为永远都是数组右边界
            max = Math.max((height.length - k - 1) * height[topIndex] , max);
        }
        return max;
    }

    public static void main(String[] args) {
        int[][] ints = new int[][]{{1,0,1,1}, {1,1,1,1}, {1,1,1,0}};
        for (int[] anInt : ints) {
            for (int i : anInt) {
                System.out.print(i);
            }
            System.out.println();
        }
        MaxSubMartrix maxSubMartrix = new MaxSubMartrix();
        int i = maxSubMartrix.maxRecSize(ints);
        System.out.println(i);
    }
}

方案二:简化版本

public class MaxSubMartrix {

    public static int getMaxSubMaxSize(int[][] arr) {
        if (arr == null || arr.length == 0 || arr[0].length == 0){
            return 0;
        }
        int[] helpeArr = new int[arr[0].length];

        int max = Integer.MIN_VALUE;
        for (int i = 0; i < arr.length; i++){
            for (int j = 0; j < arr[i].length; j++){
                helpeArr[j] = arr[i][j] == 0? 0: helpeArr[j] + arr[i][j];
            }
            max = Math.max(max, getMaxValueFromHelperArr(helpeArr));
        }
        return max;
    }
    private static int getMaxValueFromHelperArr(int[] helpeArr) {
        Stack<Integer> stack = new Stack<>();
        // linked为了好debug带顺序看,key 当前index,value为当前index左边第一个比自己小的数的index
        Map<Integer, Integer> leftMap = new LinkedHashMap<>();
        Map<Integer, Integer> rightMap = new LinkedHashMap<>();
        for (int i = 0; i < helpeArr.length; i++){
            while (!stack.isEmpty() && helpeArr[i] <= helpeArr[stack.peek()]){
                stack.pop();
            }
            if (stack.isEmpty()){
                leftMap.put(i, null);
            }else {
                leftMap.put(i, stack.peek());
            }
            stack.push(i);
        }
        stack.clear();
        for (int j = helpeArr.length-1; j >= 0; j--){
            while (!stack.isEmpty() && helpeArr[j] <= helpeArr[stack.peek()]){
                stack.pop();
            }
            if (stack.isEmpty()){
                rightMap.put(j, null);
            }else {
                rightMap.put(j, stack.peek());
            }
            stack.push(j);
        }
        int max = Integer.MIN_VALUE;
        for (int k = 0; k < helpeArr.length; k++){
            Integer left = leftMap.get(k);
            Integer right = rightMap.get(k);
            if (left == null && right == null){
                // 左右都没比它小的,就直接乘以长度即可
                max = Math.max(max, helpeArr[k] * helpeArr.length);
            }else if (left == null || right == null){
                if (left == null){
                    max = Math.max(max, helpeArr[k] * right);
                }else{
                    max = Math.max(max, helpeArr[k] * (helpeArr.length - left));
                }
            }else {
                max = Math.max(max, (right-left-1) * helpeArr[k]);
            }
        }
        return max;
    }

    public static void main(String[] args) {
        int[][] ints = new int[][]{{1,0,1,1}, {1,1,1,1}, {1,1,1,0}};
        for (int[] anInt : ints) {
            for (int i : anInt) {
                System.out.print(i);
            }
            System.out.println();
        }
        // 方案二
        int maxSubMaxSize = getMaxSubMaxSize(ints);
        System.out.println(maxSubMaxSize);
    }
}

c语言版本

正在学习中

c++语言版本

正在学习中

思考感悟

1、怎么想到的?
首先拿到题时,我没想出来用栈和队列怎么解决,但是这道题说求集合的最大值,那么只能一行一行的求每一行的最大值最后再求总的最大值,这个思想其实后面算法会遇到很多使用的地方,所以一定要记牢了。
2、怎么这么实现的?
其实在计算每一行的时候,最大值就是计算每一个柱子所能扩展的最大面积,所以还是要遍历计算每一个柱子,能拓展的最大面积还是挺容易想到的,就是计算左边和右边第一个比自己小的柱子就可以,主要注意相等的情况,如果相等的柱子不进栈会产生一个问题:
在这里插入图片描述
如图可以看到如果入栈了,index为3的计算面积是arr[3] * [5-2-1],但是如果不入栈arr[3] * [5-1-1],很显然算大了,这里是不对的

3、以后遇到什么样的题用这种思维考虑?
首先求集合中组合或者其他什么最大值的情况时,可以使用这种每一行计算最优解然后集合的情况,然后栈可以最快解决每一个元素左右边第一个比自己大/小的值的位置,这个方法很重要.

写在最后

方案和代码仅提供学习和思考使用,切勿随意滥用!如有错误和不合理的地方,务必批评指正~
如果需要git源码可咨询2260755767@qq.com或在评论区回复即可.
再次感谢左大神的书对我算法的指点迷津!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

元空间

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值