暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

数据结构和算法【88】最大的以1为边界的正方形

皮皮克克 2023-12-02
43
点击关注公众号,干货第一时间送达


这是一道 "矩阵" 类型的题目,

在企业面试中,出题率不高:

可是,不代表不出

正是因为这样,"矩阵" 类型的题目,

不多,

但是,个个经典!

能掌握精髓,便可走遍天下。

不像动态规划,必须要多刷

今天的题目,来自力扣:

原题链接:

https://leetcode.cn/problems/largest-1-bordered-square/description/


一、屏幕前的吴彦祖和刘亦菲们,请听题


举个例子:

如上图中,以 1 为边界的正方形,边长为 4,共有4*4=16个元素。

屏幕前聪明的你,

会怎么去求呢?

这题可以用动态规划,但是小编要考大家纯code能力,

不用动态规划,

怎么写代码?


二、解题

可以借助辅助矩阵,也就是 预处理矩阵

想求矩阵中边界全是1的正方形子矩阵,

显然,需要定位点:

如上图,如果咱们能求出该点向右和向下,同时能够有多少个连续的1,

是不是就可以知道最大的正方形边长了?


连续的1的个数,怎么求?

可以通过倒推。

(1)先定位到矩阵右下角,判断是否是1

利用辅助矩阵 right[][],down[][] 记录

right[][] 表示某个坐标(x,y) 向右,有多少个连续的1

down[][] 表示某个坐标(x,y) 向下,有多少个连续的1

(2)先倒推最后一列,从下往上倒推

根据right矩阵的性质,

最后一列是没有右侧元素的,所以right的值,只依赖于原矩阵grid 对应的点是否是1。

根据down矩阵的性质,down[i][j] 是依赖于下面相邻格子的值 down[i+1][j] +1

(3)再倒推最后一行,从右往左倒推

(4)最后,推导中间的位置


这样,辅助矩阵推导完毕,

原矩阵中,最大的正方形,可能有哪些?

根据正方形边长,从两个辅助矩阵求即可。


比如,原矩阵grid 边长是5,我们先判断是否有边长为5的正方形。

从 (0,0) 出发,判断right[][] 矩阵点右侧是否有连续的1的个数  >= 5的

发现没有,所以不可能存在边长为5的正方形。


边长为4的正方形呢?

可以找到一个点,向右有连续的4个1:


然后,指针下移4位,发现这个点,向右也有连续的4个1:


再判断down矩阵即可。

看看代码。



完整代码:

public class CodingDemo_03 {

    /**
     * 最大的以1为边界的正方形
     * @param grid
     * @return
     */

    public int largest1BorderedSquare(int[][] grid) {
        int M = grid.length;
        int N = grid[0].length;

        //1,创建辅助矩阵
        int[][] right = new int[M][N];
        int[][] down = new int[M][N];

        //2,填充辅助矩阵
        setBoard(grid, right, down);

        //3, 判断最大正方形
        for (int size = Math.min(M,N); size != 0; size--) {
            if (hasBoard(size, right, down)){
                return size*size; //返回这个正方形的面积
            }
        }
        return 0;
    }

    //通过辅助矩阵,判断是否存在边长为size的正方形
    private static boolean hasBoard(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++) {
                //判断向右,和向下,连续的1,是否够数
                // i+size-1 和 j+size-1 
                // 是从子矩阵左上角,跳转到左下角
                // 和从子矩阵左上角,跳转到右上角
                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;
    }

    private static void setBoard(int[][] grid, int[][] right, int[][] down){
        int M = grid.length;
        int N = grid[0].length;

        //1,先判断矩阵右下角
        if (grid[M-1][N-1] == 1){
            right[M-1][N-1] = 1;
            down[M-1][N-1] = 1;
        }

        //2, 从矩阵右下角,倒推最后一列
        for (int row = M-2; row >= 0; row--) {
            //因为此时是最后一列,right矩阵是没有右侧依赖的,所以只依赖grid
            //而down,则是依赖于下方的相邻格子
            if (grid[row][N-1] == 1){
                right[row][N-1] = 1;
                down[row][N-1] = down[row+1][N-1] + 1;
            }
        }

        //3, 从矩阵右下角,倒推最后一行
        for (int col = N-2; col >= 0; col--) {
            //同理
            if (grid[M-1][col] == 1){
                right[M-1][col] = right[M-1][col+1] + 1;
                down[M-1][col] = 1;
            }
        }

        //4,倒推中间节点
        for (int row = M-2; row >= 0; row--) {
            for (int col = N-2  ; col >= 0; col--) {
                if (grid[row][col] == 1){
                    right[row][col] = right[row][col+1] +1;
                    down[row][col] = down[row+1][col] +1;
                }
            }
        }
    }
}

去力扣试试:


结束语:
Ok,就是本篇文章的全部内容了。
如果各位有不懂的地方,欢迎发消息给小编,小编会进行详细地解答。
最后,请屏幕前的各位吴彦祖和刘亦菲们,动动你们的小手,给小编一个

文章转载自皮皮克克,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论