【中等】边界都是1的最大正方形大小-Java

本文提供了一个中等难度的编程问题,涉及寻找给定二维矩阵中边界全为1的最大正方形的边长。通过预处理技巧降低时间复杂度至O(N^3),包括计算right和down矩阵,以及使用这些矩阵加速检查过程。

分享一个大牛的人工智能教程。零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!请轻击人工智能教程大家好!欢迎来到我的网站! 人工智能被认为是一种拯救世界、终结世界的技术。毋庸置疑,人工智能时代就要来临了,科… 继续阅读 前言https://www.captainai.net/troubleshooter

package live.every.day.ProgrammingDesign.CodingInterviewGuide.ArrayAndMatrix;

/**
 * 边界都是1的最大正方形大小
 *
 * 【题目】
 * 给定一个NxN的短阵matrix,在这个矩阵中,只有0和1两种值,返回边框全是1的最大正方形的边长长度。
 * 例如:
 * 0 1 1 1 1
 * 0 1 0 0 1
 * 0 1 0 0 1
 * 0 1 1 1 1
 * 0 1 0 1 1
 * 其中,边框全是1的最大正方形的大小为4x4,所以返回4。
 *
 * 【难度】
 * 中等
 *
 * 【解答】
 * 先介绍一个比较容易理解的解法:
 * 1.矩阵中一共有NxN个位置。O(N^2)
 * 2.对每一个位置都看是否可以成为边长为N-1的正方形左上角。比如,对于(0,0)位置,依次检查是否是边长为5的正方形左上角,然
 * 后检查边长为4、3等。O(N)
 * 3.如何检查一个位置是否可以成为边长为N的正方形的左上角呢?遍历这个边长为N的正方形边界看是否只由1构成,也就是走过4个边的
 * 长度(4N)。O(N)
 * 所以普通方法总的时间复杂度为O(N^2)xO(N)xO(N)=O(N^4)。
 *
 * 本文提供的方法的时间复杂度为O(N^3),基本过程也是如上三个步骤。但是对于步骤3,可以把时间复杂度由O(N)降为O(1)。具体地
 * 说,就是能够在O(1)的时间内检查一个位置假设为(i,j),是否可以作为边长为a(1<=a<=N)的边界全是1的正方形左上角。关键是使
 * 用预处理技巧,这也是面试经常使用的技巧之一,下面介绍得到预处理矩阵的过程。
 *
 * 1.预处理过程是根据矩阵matrix得到两个矩阵right和down。right[i][j]的值表示从位置(i,j)出发向右,有多少个连续的1。
 * down[i][j]的值表示从位置(i,j)出发向下有多少个连续的1。
 *
 * 2.right和down矩阵如何计算?
 * 1)从矩阵的右下角(n-l,n-1)位置开始计算,如果matrix[n-1][n-1]==1,那么,right[n-1][n-1]=1且down[n-1][n-1]=1,
 * 否则都等于0。
 * 2)从右下角开始往上计算,即在matrix最后一列上计算,位置就表示为(i,n-1)。对right矩阵来说,最后一列的右边没有内容,所
 * 以,如果matrix[i][n-1]==1,则令right[i][n-1]=1,否则为0。对down矩阵来说,如果matrix[i][n-1]==1,因为
 * down[i+1][n-1]表示包括位置(i+1,n-1)在内并往下有多少个连续的1,所以,如果位置(i,n-1)是1,那么,令down[i][n-1]
 * =down[i+1][n-1]+1;如果matrix[i][n-1]==0,则令down[i][n-1]=0。
 * 3)从右下角开始往左计算,即在matrix最后一行上计算,位置可以表示为(n-1,j)。对right矩阵来说,如果matrix[n-1][j]==1,
 * 因为right[n-1][j+1]表示包括位置(n-1,j+1)在内右边有多少个连续的1。所以,如果位置(n-1,j)是1,则令right[n-1][j]
 * ==right[n-1][j+1]+1;如果matrix[n-1][j]==0,则令right[n-1][j]==0。对down矩阵来说,最后一列的下边没有内容,
 * 所以,如果matrix[n-1][j]==1,令down[n-1][j]=1,否则为0。
 * 4)计算完步骤1)~步骤3)之后,剩下的位置都是既有右,也有下,假设位置表示为(i,j):
 * 如果matrix[i][j]==1,则令right[i][j]=right[i][j+1]+1,down[i][j]=down[i+1][j]+1。
 * 如果matrix[i][j]==0,则令right[i][j]==0,down[i][j]==0。
 *
 * 预处理的具体过程请参看如下代码中的setBorderMap方法。
 *
 * 得到right和down矩阵后,如何加速检查过程呢?比如现在想检查一个位置,假设为(i,j)。是否可以作为边长为a(1<=a<=N)的边
 * 界全为1的正方形左上角。
 * 1)位置(i,j)的右边和下边连续为1的数量必须都大于或等于a(right[i][j]>=a&&down[i][j]>=a),否则说明上边界和左边界的
 * 1不够。
 * 2)位置(i,j)向右跳到位置(i,j+a-1),这个位置是正方形的右上角,那么这个位置的下边连续为1的数量也必须大于或等于
 * a(down[i][j+a-1]>=a),否则说明右边界的1不够。
 * 3)位置(i,j)向下跳到位置(i+a-l,j),这个位置是正方形的左下角,那么这个位置的右边连续为1的数量也必须大于或等于
 * a(right[i+a-1][j]>=a),否则说明下边界的1不够。
 * 以上三个条件都满足时,就说明位置(i,j)符合要求,利用right和down矩阵之后,加速的过程很明显,不需要遍历边长上的所有值
 * 了,只看4个点即可。
 *
 * 全部过程请参看如下代码中的getMaxSize方法。
 *
 * @author Created by LiveEveryDay
 */

public class Border1MaxSquareSize {

    public static void setBorderMap(int[][] m, int[][] right, int[][] down) {
        int r = m.length;
        int c = m[0].length;
        if (m[r - 1][c - 1] == 1) {
            right[r - 1][c - 1] = 1;
            down[r - 1][c - 1] = 1;
        }
        for (int i = r - 2; i != -1; i--) {
            if (m[i][c - 1] == 1) {
                right[i][c - 1] = 1;
                down[i][c - 1] = down[i + 1][c - 1] + 1;
            }
        }
        for (int i = c - 2; i != -1; i--) {
            if (m[r - 1][i] == 1) {
                right[r - 1][i] = right[r - 1][i + 1] + 1;
                down[r - 1][i] = 1;
            }
        }
        for (int i = r - 2; i != -1; i--) {
            for (int j = c - 2; j != -1; j--) {
                if (m[i][j] == 1) {
                    right[i][j] = right[i][j + 1] + 1;
                    down[i][j] = down[i + 1][j] + 1;
                }
            }
        }
    }

    public static int getMaxSize(int[][] m) {
        int[][] right = new int[m.length][m[0].length];
        int[][] down = new int[m.length][m[0].length];
        setBorderMap(m, right, down);
        for (int size = Math.min(m.length, m[0].length); size != 0; size--) {
            if (hasSizeOfBorder(size, right, down)) {
                return size;
            }
        }
        return 0;
    }

    public static boolean hasSizeOfBorder(int size, int[][] right, int[][] down) {
        for (int i = 0; i != right.length - size + 1; i++) {
            for (int j = 0; j != right[0].length - size + 1; j++) {
                if (right[i][j] >= size
                        && down[i][j] >= size
                        && right[i + size - 1][j] >= size
                        && down[i][j + size - 1] >= size) {
                    return true;
                }
            }
        }
        return false;
    }

    public static void main(String[] args) {
        int[][] matrix = new int[][]{
                {0, 1, 1, 1, 1},
                {0, 1, 0, 0, 1},
                {0, 1, 0, 0, 1},
                {0, 1, 1, 1, 1},
                {0, 1, 0, 1, 1,}};
        System.out.printf("The max size is: %d", getMaxSize(matrix));
    }

}

// ------ Output ------
/*
The max size is: 4
*/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值