一个矩阵matrix,内部元素是0或者1,返回边框为1的最大的正方形的长是多少

一个矩阵matrix,内部元素是0或者1,返回边框为1的最大的正方形的长是多少?

提示:利用空间换时间,这是大多数数据结构与算法的优化思路,尤其是设计数组,矩阵等
之前用空间换时间的,往往涉及前缀累加和,前缀统计量,本题也是前缀后缀统计量
提前准备好这个信息,外围调度就是暴力搜索的调度,内部就把寻找优化为o(1)速度

类似的空间换时间的经典优化算法:
【1】一排正方形,有红色绿色两种颜色,将左边全变红色,右边全变绿色,最少需要染色的个数是多少


题目

一个矩阵matrix,内部元素是0或者1,返回边框为1的最大的正方形的长是多少?


一、审题

示例:
arr=
01111
01001
01001
01111
01011
下面这个边框全是1
最大边框全是1的正方形的边长为4
xiam


关于矩阵长方形的重要基础知识:不同的长方形有N的4次方个,不同的正方形有N的3次方个

在一个给定的矩阵的,你枚举不同的
(1)长方形有:N的4次方个
(2)正方形有:N的3次方个
为啥呢?
给你一个矩阵:如何确定一个长方形,或者正方形呢?
(1)长方形:2个点确定一个长方形,即左上角点:x1,y1,右下角点:x2,y2
你在矩阵中确定x1,y1,x2,y2,每一个数都是N种随机的情况,自然就是N的4次方种不同的情况
所以组成的长方形就是N^4个
在这里插入图片描述

(2)正方形:1个点和正放心干的边长b确定
你在矩阵中确定x1,y1,每一个数都是N种随机的情况,自然就是N的2次方种不同的情况
再决定一个随机的边长b,有N种情况
所以组成的正方形就是N^3个
在这里插入图片描述

不论你今后操作正方形,长方形,是暴力解,还是优化解,起码外围的调度N^3或者N的4次方,是饶过不去的
只不过就看你拿到正方形,长方形之后,还要去统计它的面积呢?边都是某种数字呢?还是某一个参数
所用的方法是暴力搜索呢?还是空间换时间,加速搜搜!

这就是优化的侧重点!!!
暴力肯定不行的,本身调度就是o(n的3或者4次方)
你再暴力o(n)搜索,恐怕复杂度就炸了

显然优化的时候,你得想办法用额外空间信息,提前准备预设数组信息,来加速和这个搜索过程,o(1)复杂度能拿到信息
完成加速优化的过程!

本题只看正方形!


暴力解:除了o(n^3)复杂度调度外,还需要O(n)判断边框是否全1

上面说了
除了o(n^3)复杂度调度外,还需要O(n)判断边框是否全1
来到左上角xy,还需要看四个变b他们是否都全1?
在这里插入图片描述
(1)从xy点到右上角x,y+b-1,o(n)看看他们是否都是1?【下图绿色线条】
在这里插入图片描述
(2)从xy点到左下角x+b-1,y,o(n)看看他们是否都是1?【下图橘色线条】
在这里插入图片描述
(3)从左下角点x+b-1,y到右下角点x+b,y+b-1,o(n)看看他们是否都是1?【下图粉色线条】
在这里插入图片描述
(4)从右上角点x,y+b-1到右下角点x+b,y+b-1,o(n)看看他们是否都是1?【下图蓝色线条】
在这里插入图片描述
由于是并行判断四条线o(4n)=o(n)
故整体调度之下来这四种判断,复杂度为o(n^4)


二、最优解:除了o(n^3)复杂度调度外,提前准备某个方向边框为全1的长度信息,只需要O(1)判断边框是否全1

我们的目标是把上面的判断边框四边全1的事,直接o(1)搞定
这样优化之后的整体复杂度就是o(n^3)

如何直接o(1)搞定判断边框四边全1的事呢?
咱们看下图,有这么个情况:
(1)用right数组,表示arr的xy位置及其右边有几个连续的1
(2)用down数组,表示arr的xy位置及其下边有几个连续的1
比如:
在这里插入图片描述
这样的话,有了xy左上角,和正方形的边长b
那就可以确定正方形的四个交点
比如A,B,C,D
重要ABD满足下面四个条件:
right[A]>=b && right[B]>=b && down[A] >= b && down[D] >= b
说经边框的四个变边都是1,这样就是全1边框,这个判断条件完全就是o(1)速度秒杀拿到的。
在这里插入图片描述
具体咋填写呢?
(1)right:咱的目的就是来到ij看右边的情况,然后看连续1的个数,中途遇到矩阵有一个0,从此清零来过。
很简单,对于right,首先需要看最后一列,如果matrix[i][j]是1,right[i][j]就填1,
剩下前面所有列,如果j列matrix是0,则right[i][j]就是0,如果j列matrix是1,则right[i][j]=right[i][j+1]+1

在这里插入图片描述
(2)down:咱的目的就是来到ij看下边的情况,然后看连续1的个数,中途遇到矩阵有一个0,从此清零来过。
很简单,对于down,首先需要看最后一行,如果matrix[i][j]是1,down[i][j]就填1,
剩下前面所有行,如果i行matrix是0,则down[i][j]就是0,如果i行matrix是1,则down[i][j]=down[i+1][j]+1
在这里插入图片描述

好,如果有了right和down数组,
那么,看外围调度,就看N^3个正方形,哪个正方形最大?而且满足上述边框是全1的条件?最大的边框为1的边长b就是我们要的结果。

我们从边长b最大的那个正方形开始枚举!

矩形如果matrix的长为N*M
**min(M,N)**是长方形的最大边长,因为再长恐怕就是越界了
当然b>0,否则正方形不存在的
在这里插入图片描述
每一个b,
枚举左上角的位置i行,j列;
i行从0–N-b+1,j列从0–M-b+1取值的左上角点,这个边框,是否满足全1?
第一个全1就是咱们要的边长b

为啥i和j最大不能越过N-b+1或者M-b+1?
因为变成固定b了,i超过N-b+1的话,或者j超过M-b+1的话,恐怕正方形就出界了
在这里插入图片描述

这样的话,我们就把判断边框是否全1的函数单独列出来,然后主函数枚举边长b的正方形,看看谁满足条件

首先,我们根据题目给的矩形matrix,填写预设数组:right和down数组,

    //(1)用right数组,表示arr的xy位置及其**右边**有几个连续的1
    //(2)用down数组,表示arr的xy位置及其**下边**有几个连续的1
    public static void writeRightAndDown(int[][] matrix, int[][] right, int[][] down){
        //right,自然要看最右那列,然后往左推每个位置
        int N = matrix.length;
        int M = matrix[0].length;
        //很简单,对于right,首先需要看最后一列,如果matrix[i][j]是1,right[i][j]就填1,
        //剩下前面所有列,如果j列matrix是0,则right[i][j]就是0,
        // 如果j列matrix是1,则right[i][j]=right[i][j+1]+1
        for (int i = 0; i < N; i++) {
            right[i][M - 1] = matrix[i][M - 1] == 1 ? 1 : 0;
        }
        for (int j = M - 2; j >= 0; j--) {
            for (int i = 0; i < N; i++) {
                //外边调度列,内部调度行,以列一列往左填
                right[i][j] = matrix[i][j] == 1 ? right[i][j + 1] + 1 : 0;
            }
        }
        //很简单,对于down,首先需要看最后一行,如果matrix[i][j]是1,down[i][j]就填1,
        //剩下前面所有行,如果i行matrix是0,则down[i][j]就是0,
        // 如果i行matrix是1,则down[i][j]=down[i+1][j]+1
        for (int j = 0; j < M; j++) {
            down[N - 1][j] = matrix[N - 1][j] == 1 ? 1 : 0;
        }
        for (int i = N - 2; i >= 0; i--) {
            for (int j = 0; j < M; j++) {
                //外面是调度行,内部调度列,从下往上填
                down[i][j] = matrix[i][j] == 1 ? down[i + 1][j] + 1: 0;
            }
        }
    }

如果给定你边长b,right和down数组,不管你的左上角点在哪,这种边长b能有其中一个正方形是合法的吗???
注意哦!!!不管正方形的左上角点在哪,只看边长哦!!!有其一合法即可。
请你单独判断这个矩形的四个边框是否全1?

必须捣鼓清楚下标的边界!

    //如果给定你边长b,right和down数组,不管你的左上角点在哪,这种边长b能有其中一个正方形是合法的吗???
    //注意哦!!!**不管正方形的左上角点在哪,只看边长哦!!!有其一合法即可。**
    //请你单独判断这个矩形的四个边框是否全1?
    public static boolean oneOfSquareIsValid(int b, int[][] right, int[][] down){
        //枚举不同的左上角,
        // i行从0--N-b+1,
        // j列从0--M-b+1取值的左上角点,
        // 看看边长为b的这个边框,是否满足全1?
        int N = right.length;
        int M = right[0].length;
        //为啥i和j最大不能越过N-b+1或者M-b+1?
        //因为变成固定b了,i超过N-b+1的话,或者j超过M-b+1的话,**恐怕正方形就出界了**!
        for (int i = 0; i < N - b + 1; i++) {
            for (int j = 0; j < M - b + 1; j++) {//一定要捣鼓清楚这些下标
                //ABCD中达标的A,B,D仨点的各自right和down达标就行
                if (right[i][j] >= b && right[i + b - 1][j] >= b && 
                        down[i][j] >= b && down[i][j + b - 1] >= b) return true;
            }
        }
        return false;//全部找了都不行,没有提前返回true
    }

okay,那么我们就可以来枚举边长了,b从min(N,M)–1枚举,每一个边长,是否存在某种边长b的哪个矩形(左上角随意的)
达标,一旦达标,b就是咱们的结果:

    //okay,那么我们就可以来枚举边长了,b从min(N,M)--1枚举,每一个边长,是否存在某种边长b的哪个矩形(左上角随意的)
    //达标,一旦达标,b就是咱们的结果:
    public static int maxBSize(int[][] matrix){
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) return 0;

        //枚举不同的边长
        int N = matrix.length;
        int M = matrix[0].length;

        int[][] right = new int[N][M];
        int[][] down = new int[N][M];
        //(1)用right数组,表示arr的xy位置及其**右边**有几个连续的1
        //(2)用down数组,表示arr的xy位置及其**下边**有几个连续的1
        writeRightAndDown(matrix, right, down);
        for (int b = Math.min(N, M); b >= 1; b--) {
            if (oneOfSquareIsValid(b, right, down)) return b;
            //有了right,down,其实matrix没用了,只看b
        }

        return 0;//如果一直没有找到,返回0
    }

测试一把

    public static void test(){
        int[][] matrix = {
                {1,1,0,0,1,0,1},
                {0,1,1,1,1,0,1},
                {0,1,0,0,1,1,0},
                {0,1,0,0,1,0,1},
                {0,1,1,1,1,1,1}
        };

//        int[][] right = new int[matrix.length][matrix[0].length];
//        int[][] down= new int[matrix.length][matrix[0].length];
//        setBorderMap(matrix, right, down);
//        printMatrix(right);
//        System.out.println();
//        printMatrix(down);

        System.out.println(getMaxSize(matrix));

        printMatrix(matrix);
        System.out.println();
        System.out.println(maxBSize(matrix));
    }

    public static void printMatrix(int[][] arr){
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr[0].length; j++) {
                System.out.print(arr[i][j] +" ");
            }
            System.out.println();
        }
    }


    public static void main(String[] args) {
        test();
    }

问题不大:

4
matrix
1 1 0 0 1 0 1 
0 1 1 1 1 0 1 
0 1 0 0 1 1 0 
0 1 0 0 1 0 1 
0 1 1 1 1 1 1 
right
2 1 0 0 1 0 1 
0 4 3 2 1 0 1 
0 1 0 0 2 1 0 
0 1 0 0 1 0 1 
0 6 5 4 3 2 1 
down
1 5 0 0 5 0 2 
0 4 1 1 4 0 1 
0 3 0 0 3 1 0 
0 2 0 0 2 0 2 
0 1 1 1 1 1 1 
最大边框为全1的正方形的边长为:
4

总结

提示:重要经验:

1)利用空间换时间,这是大多数数据结构与算法的优化思路,尤其是设计数组,矩阵等,提前准备好前缀统计信息,外围调度就是暴力搜索的调度,内部就把寻找优化为o(1)速度
2)本题枚举每一种可能的最大边长b,然后随机看不同的左上角,也就是不同的正方形,有一个正方形合格,就返回b
3)笔试求AC,可以不考虑空间复杂度,但是面试既要考虑时间复杂度最优,也要考虑空间复杂度最优。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

冰露可乐

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

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

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

打赏作者

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

抵扣说明:

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

余额充值