leetcode
leetcode 1051 ~ 1100
最大的以 1 为边界的正方形

最大的以 1 为边界的正方形

难度:

标签:

题目描述

代码结果

运行时间: 43 ms, 内存: 16.5 MB


/*
 * 思路:
 * Java Stream不适合用来处理二维数组的动态规划问题,因此这里采用基本相同的思路来实现算法,
 * 但是无法完全集成Java Stream来实现。主要思路是利用动态规划思想和条件判断来寻找最大正方形边长。
 */

import java.util.stream.IntStream;

public class SolutionStream {
    public int largest1BorderedSquare(int[][] grid) {
        int rows = grid.length;
        int cols = grid[0].length;
        int[][] top = new int[rows + 1][cols + 1];
        int[][] left = new int[rows + 1][cols + 1];
        int maxLen = 0;

        IntStream.range(1, rows + 1).forEach(i -> {
            IntStream.range(1, cols + 1).forEach(j -> {
                if (grid[i - 1][j - 1] == 1) {
                    top[i][j] = top[i - 1][j] + 1;
                    left[i][j] = left[i][j - 1] + 1;
                    int len = Math.min(top[i][j], left[i][j]);
                    while (len > 0) {
                        if (left[i - len + 1][j] >= len && top[i][j - len + 1] >= len) {
                            maxLen = Math.max(maxLen, len);
                            break;
                        }
                        len--;
                    }
                }
            });
        });
        return maxLen * maxLen;
    }
}

解释

方法:

该题解采用动态规划的思想。首先,创建两个二维数组row和col,其中row[i][j]表示在grid[i][j]处,从该点向左连续的1的数量;col[i][j]表示在grid[i][j]处,从该点向上连续的1的数量。然后,遍历整个网格,对于每一个点(grid[i][j] == 1),我们检查以该点为右下角的正方形的最大边长。这里的最大边长取决于从该点向左和向上连续的1的数量(即row[i][j]和col[i][j]的较小值)。对于可能的每个边长,我们检查相应的左边界和上边界是否满足条件(即是否包含足够数量的连续的1)。如果满足条件,我们更新最大正方形的边长。最后,返回最大边长的平方,即最大正方形中的元素数量。

时间复杂度:

O(mn * min(m, n))

空间复杂度:

O(mn)

代码细节讲解

🦆
在初始化row和col数组时,为什么只需要考虑向左或向上的连续1的数量,而不是四个方向?
在这个算法中,我们的目标是找到以某点为右下角的最大正方形。因此,每个格子只需要知道从它向左以及向上能延伸出多少连续的1。这是因为正方形的验证从右下角开始,首先检查左侧和上侧的连续性。向右和向下的连续性在这个场景中不直接贡献于形成以当前点为右下角的正方形边界。
🦆
动态规划解决这个问题的关键在哪里?具体是如何通过前面的状态推导出后面状态的?
动态规划的关键在于使用先前计算的状态(连续的1的数量)来帮助快速计算后续状态,避免重复计算。对于每个点grid[i][j],我们根据它的左边点(grid[i][j-1])和上边点(grid[i-1][j])的状态来更新当前点的row和col数组。如果grid[i][j]是1,那么row[i][j]就是row[i][j-1] + 1,col[i][j]就是col[i-1][j] + 1。这种方法使得我们能够在处理每个点时快速得出从该点向左和向上的连续1的数量。
🦆
你是如何确定检查每个可能的边长时的停止条件的?为什么是在当前最大边长大于等于max_l时才停止?
检查每个可能的边长时,我们从当前点能形成的最大可能边长(即row[i][j]和col[i][j]中较小的那个)开始向下逐步检查,直到边长小于已知的最大边长max_l。这样做是因为,如果当前正在检查的边长已经小于我们之前找到的最大边长max_l,那么即使这个边长是有效的,它也不会帮助我们得到一个更大的正方形。因此,这个停止条件帮助我们省去了不必要的计算,并尽快地跳出循环。
🦆
为什么在检查左边界和上边界是否包含足够数量的连续的1时,只需要检查row[x][j]和col[i][y]?
因为row[x][j]代表从点(x, j)开始向左的连续1的数量,而col[i][y]表示从点(i, y)开始向上的连续1的数量。在检查一个潜在的边长是否可以形成正方形时,我们需要确保左边界和上边界的长度至少与当前考虑的边长相等。检查row[x][j]可以保证从右下角的正方形的左上角到左下角的水平线段是连续的1,检查col[i][y]可以保证从右下角的正方形的左上角到右上角的垂直线段是连续的1。这两个检查确保了正方形的左边界和上边界是完整的。

相关问题