算法48:动态规划专练(力扣221:最大正方形面积)

题目:

在一个由 '0' 和 '1' 组成的二维矩阵内,找到只包含 '1' 的最大正方形,并返回其面积。

示例 1:

输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
输出:4

示例 2:

输入:matrix = [["0","1"],["1","0"]]
输出:1

示例 3:

输入:matrix = [["0"]]
输出:0

我之前写过一篇博客,是利用单调栈计算最大矩形面积的。感兴趣的看一下:算法37:最大矩形面积(力扣84、85题)---单调栈-CSDN博客

而本道题目求的是最大正方形面积,因此,利用单调栈完全可以解决。思路和求最大矩形面积是一样的,下面直接贴出单调栈的代码:

单调栈解法:

package code04.动态规划专项训练02;

/**
 *  力扣 221题  最大正方形面积
 *  https://leetcode.cn/problems/maximal-square/?envType=study-plan-v2&envId=dynamic-programming
 */
public class MaximalSquare_03_leetcode221单调栈 {

    public int maximalSquare(char[][] matrix) {

        if (matrix == null || matrix.length == 0) {
            return 0;
        }

        int[] dp = new int[matrix[0].length];
        int res = 0;
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[i].length; j++) {
                //数组压缩
                int cur = matrix[i][j] == '0' ? 0 : Integer.parseInt(String.valueOf(matrix[i][j]));
                dp[j] = cur == 0 ? 0 : dp[j] + cur;
            }
            res = Math.max(res, getMaxValue(dp));
        }

        return res;
    }

    public int getMaxValue (int[] arr) {

        int result = 0;
        int size = arr.length;
        //自定义栈结构
        int[] stack = new int[arr.length];
        int stackSize = 0;

        for (int i = 0; i < arr.length; i++) {

            while (stackSize != 0 && arr[stack[stackSize-1]] >= arr[i]) {
                //当前弹出数的下标
                int curIndex = stack[--stackSize];
                //左边第一个比curIndex数组对应数小的下标
                int leftIndex = stackSize == 0 ? -1 : stack[stackSize -1];
                //右边第一个比curIndex数组对应数小的下标
                int rightIndex = i;

                int width = Math.min(arr[curIndex], rightIndex - leftIndex - 1);

                result = Math.max(result, width * width);
            }

            stack[stackSize++] = i;
        }

        //如果还有剩余
        while (stackSize != 0) {
            //当前弹出数的下标
            int curIndex = stack[--stackSize];

            //左边第一个比curIndex数组对应数小的下标
            int leftIndex = stackSize == 0 ? -1 : stack[stackSize -1];

            int width = Math.min(arr[curIndex], size - leftIndex - 1);

            result = Math.max(result, width * width);
        }

        return result;
    }

    public static void main(String[] args) {
        MaximalSquare_03_leetcode221单调栈 ss = new MaximalSquare_03_leetcode221单调栈();
        char[][] matrix = {{'1','0','1','0','0'},{'1','0','1','1','1'},{'1','1','1','1','1'},{'1','0','0','1','0'}};
        System.out.println(ss.maximalSquare(matrix));
    }
}

 

胜率虽然不高,但是只花费了 12毫秒, 还算可以的了。

本章是动态规划专练,因此,不得不说一下动态规划的思路:

1. 我们知道,正方是有4个顶点,并且每条边的边长都是相等的。

2. 本道题规定,如果每个单元格的值为1,那么它最小也可以看成是变成为1的正方形。如果为0,当前单元格就不能算作正方形。

3. 如果只有4个顶点,想要组成最大边长为2的正方形,那么这4个顶点必定全部都为1.  如果以最后一个顶点为正方形的结束点,那么依赖的其他三个点,必定全部不为0.

 

X1X3
X2

1 + Math.min(X1, Math.min(X2, X3))

(依赖左、上、左上3个顶点的最小值)

也就是说,目前看到的这些节点,以当前单元格

结束,那么它的最大边长就是1 + Math.min(X1, Math.min(X2, X3))

为什么要依赖最小值?

因为,X1、X2、X3任何一点为0, 那么以当前单元格为正方形的结束节点,那么它的最大正方形边长就为1. 当然,当前单元格的值必须不为0.

4. 第一行,正方形的边长最大为1; 第一列,最大正方形的边长也为1. 因为正方形边长是需要相等的。

我们根据事例1给的数据进行推导:

第一步:

下标01234
下标010100
11

原始数组为0,

无法参与到正

方形的结束点.

直接取0

前一列为0,

上一列为1.取小。

当前列为1. 

因此, 1 + 0 = 1

1 + 0 = 11 + 0 = 1
21
31

第二步:根据上方规则,类推

下标01234
下标010100
11

0

1

11
211 + 0 = 11 + 1 = 21+ 1 = 21+ 1 = 2
31

第三步:根据上方规则,类推

下标01234
下标010100
11

0

1

11
211222
31001+0 = 10

 目测就知道,最大正方形的边长为2. 面积为 4;

动态规划代码:

package code04.动态规划专项训练02;

/**
 * 力扣 221题  最大正方形面积
 * https://leetcode.cn/problems/maximal-square/?envType=study-plan-v2&envId=dynamic-programming
 */
public class MaximalSquare_03_leetcode221动态规划 {

    public int maximalSquare(char[][] matrix) {

        if (matrix == null || matrix.length == 0) {
            return 0;
        }

        int row = matrix.length;
        int col = matrix[0].length;

        int[][] dp = new int[row][col];
        int maxSide = 0;
        //第一行
        for (int i = 0; i < col; i++) {
            dp[0][i] = matrix[0][i] == '0' ? 0 : 1;

            if (maxSide == 0 && dp[0][i] > 0) {
                maxSide = 1;
            }
        }

        //第一列
        for (int i = 0; i < row; i++) {
            dp[i][0] = matrix[i][0] == '0' ? 0 : 1;

            if (maxSide == 0 && dp[i][0] > 0) {
                maxSide = 1;
            }
        }

        for (int index1 = 1; index1 < row; index1++) {
            for (int index2 = 1; index2 < col; index2++) {
                if (matrix[index1][index2] == '1') {
                    //左上顶点
                    int p1 = dp[index1 - 1][index2 - 1];
                    //左顶点
                    int p2 = dp[index1][index2 - 1];
                    //上顶点
                    int p3 = dp[index1 - 1][index2];

                    dp[index1][index2] = Math.min(p3, Math.min(p1, p2)) + 1;
                } else {
                    dp[index1][index2] = 0;
                }

                maxSide = Math.max(maxSide, dp[index1][index2]);
            }
        }
        return maxSide * maxSide;
    }

    public static void main(String[] args) {
        MaximalSquare_03_leetcode221动态规划 ss = new MaximalSquare_03_leetcode221动态规划();
        //char[][] matrix = {{'1', '0', '1', '0', '0'}, {'1', '0', '1', '1', '1'}, {'1', '1', '1', '1', '1'}, {'1', '0', '0', '1', '0'}};
        //char[][] matrix = {{'0', '1'}, {'1', '0'}};
        char[][] matrix = {{'0', '1'}};
        System.out.println(ss.maximalSquare(matrix));
    }
}

它的确比单调栈更快,效率更高。而且,它的思路更加的简单。

但是,它只能解决正方形的问题,如果是矩形,当前思路都无法解决。因此,他有一定的局限性。

下一道题,同样也是算最大正方形面积的题目。但是,单调栈、本章的动态规划推导思路完全不适用,只能用暴力方法去解决。因此,本道题的暴力方法,也必须得介绍一下。

暴力解法:

1. 遍历全部单元格,找出每个单元格作为正方形顶点的最大边长。

2. 在步骤1找的过程中,是范围内的验证。

暴力解法思路相对简单,只是代码比较复杂

package code04.动态规划专项训练02;

/**
 * 力扣 221题  最大正方形面积
 * https://leetcode.cn/problems/maximal-square/?envType=study-plan-v2&envId=dynamic-programming
 * <p>
 * 基本思路:
 * 1. 当前列为正方形起点。
 * 从左往右,推算出最大边长; 从上往下推算出最大边长; 因为是正方形,因此之前两个边长取小
 * 2. 对这个范围内的所有格子进行为1验证
 */
public class MaximalSquare_03_leetcode221暴力解 {

    public int maximalSquare(char[][] matrix) {

        if (matrix == null || matrix.length == 0) {
            return 0;
        }

        int row = matrix.length;
        int col = matrix[0].length;
        //最大边长
        int max = 0;
        for (int i = 0; i < row ; i++) {
            //当前行的每一列都作为正方形的左上角,即起始点
            for (int j = 0; j < col; j++) {
                //当前格子是否为1,为1才可能成为正方形的起始点
                if (matrix[i][j] == '0') {
                    continue;
                }
                //整个二维数组中,重要有1出现,那正方形边长至少为1
                if (max == 0) {
                    max = 1;
                }
                //行边长,列边长,两者取小。因为是正方形
                int p = Math.min(row - i, col - j);
                //从i,j开始 到 i+index,j+index 范围内。全部都为1,才能验证通过
                //start为默认边长,默认边长为1. 因为matrix[i][j] == 1
                for (int count = 1; count < p; count++) {
                    //斜线
                    if (matrix[i + count][j+count] == '0') {
                        break;
                    }

                    boolean flag = true;
                    // 如果上边行、左边列延伸都不为0;那就继续校验正方形
                    // 内部是否全为1
                    for (int m = 0; m < count; m++) {
                        if (matrix[i + count][j + m] == '0' || matrix[i + m][j + count] == '0') {
                            flag = false;
                            break;
                        }
                    }

                    if (flag) {
                        //默认边长为1, 即当前正方形做顶点为1
                        //count是新增的长度。 count+1 代表正常放边长
                        max = Math.max(max, count + 1);
                    }
                    else {
                        break;
                    }
                }

            }
        }
        return max * max;
    }

    public static void main(String[] args) {
        MaximalSquare_03_leetcode221暴力解 ss = new MaximalSquare_03_leetcode221暴力解();
        //char[][] matrix = {{'1', '0', '1', '0', '0'}, {'1', '0', '1', '1', '1'}, {'1', '1', '1', '1', '1'}, {'1', '0', '0', '1', '0'}};
        char[][] matrix = {{'0', '1'}, {'1', '0'}};
        System.out.println(ss.maximalSquare(matrix));
    }
}

因此,本道题使用单调栈、动态规划性能更高。暴力解法,性能低。因为下一道题,只能基于暴力解法完成并优化,因此,不得不提前在此提一下暴力解法。

  • 12
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述: 给你两个版本号 version1 和 version2 ,请你比较它们。 版本号由一个或多个修订号组成,各修订号由一个 '.' 连接。每个修订号由多位数字组成,可能包含前导零。每个版本号至少包含一个字符。修订号从左到右编号,下标从0开始,最左边的修订号下标为0 ,下一个修订号下标为1,以此类推。例如,2.5.33 和 0.1 都是有效的版本号。 比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较忽略任何前导零后的整数值。也就是说,修订号1和修订号001相等。如果版本号没有指定某个下标处的修订号,则该修订号视为0。例如,版本1.0 小于版本1.1,因为它们下标为0的修订号相同,而下标为1的修订号分别为0和1,0 < 1。 返回规则如下: 如果 version1 > version2 返回 1, 如果 version1 < version2 返回 -1, 否则返回 0。 示例 1: 输入:version1 = "1.01", version2 = "1.001" 输出:0 解释:忽略前导零,"01" 和 "001" 都表示相同的整数 "1" 示例 2: 输入:version1 = "1.0", version2 = "1.0.0" 输出:0 解释:version1 没有指定下标为 2 的修订号,即视为 "0" 示例 3: 输入:version1 = "0.1", version2 = "1.1" 输出:-1 解释:version1 中下标为 0 的修订号是 0,version2 中下标为 0 的修订号是 1 。0 < 1,所以 version1 < version2 示例 4: 输入:version1 = "1.0.1", version2 = "1" 输出:1 示例 5: 输入:version1 = "7.5.2.4", version2 = "7.5.3" 输出:-1 提示: 1 <= version1.length, version2.length <= 500 version1 和 version2 仅包含数字和 '.' version1 和 version2 都是 有效版本号

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值