【LeetCode】动态规划—221. 最大正方形(附完整Python/C++代码)

前言

在二维矩阵中寻找最大正方形的问题是动态规划的一个经典应用。这个问题不仅考察我们对二维数组的操作,还需要我们理解如何通过递推公式优化解法。矩阵中的每个元素都可能成为一个正方形的一部分,而我们要做的就是利用之前的计算结果,在矩阵的每个位置处找到能够扩展出的最大正方形。

本文将介绍如何通过动态规划方法解决这一问题。我们会逐步分析递推关系,并通过 Python 和 C++ 代码示例展示具体的实现,并在最后总结代码的实现细节。

题目描述

在这里插入图片描述

基本思路

1. 问题定义:

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

2. 理解问题和递推关系:

  • 该问题的本质是求解在矩阵中可以组成最大正方形的边长,并最终返回其面积。
  • 假设 d p [ i ] [ j ] d p[i][j] dp[i][j] 表示以位置 ( i , j ) (i, j) (i,j) 为右下角的最大正方形的边长。为了构成一个更大的正方形,要求该位置的左边、上边和左上角位置的 d p d p dp 值都要有足够的边长。因此,状态转移方程为:

d p [ i ] [ j ] = min ⁡ ( d p [ i − 1 ] [ j ] , d p [ i ] [ j − 1 ] , d p [ i − 1 ] [ j − 1 ] ) + 1 d p[i][j]=\min (d p[i-1][j], d p[i][j-1], d p[i-1][j-1])+1 dp[i][j]=min(dp[i1][j],dp[i][j1],dp[i1][j1])+1

其中, d p [ i − 1 ] [ j ] d p[i-1][j] dp[i1][j] 表示上边的正方形边长, d p [ i ] [ j − 1 ] d p[i][j-1] dp[i][j1] 表示左边的正方形边长, d p [ i − 1 ] [ j − 1 ) d p[i-1][j- 1) dp[i1][j1表示左上角的正方形边长。三者的最小值加上当前的 1 1 1 就可以组成一个新的正方形。

  • 边界条件:如果当前位置的值是 ’ 0 ′ 0^{\prime} 0 ,那么 d p [ i ] [ j ] = 0 d p[i][j]=0 dp[i][j]=0 ,因为无法形成任何正方形。

3. 解决方法:

3.1 动态规划方法

  1. 创建一个二维数组 d p , d p [ i ] [ j ] d p, d p[i][j] dp,dp[i][j] 表示以 m a t r i x [ i ] [ j ] matrix[i][j] matrix[i][j] 为右下角的最大正方形的边长。
  2. 初始化 d p d p dp 的第一行和第一列,直接等于矩阵的第一行和第一列的值,因为这些位置无法通过左上、上、左来扩展。
  3. 从第二行、第二列开始,依次计算 d p [ i ] [ j ] d p[i][j] dp[i][j] ,并记录最大正方形的边长。
  4. 结果为最大边长的平方,即最大正方形的面积。

3.2 空间优化的动态规划

  • 由于每次计算 d p [ i ] [ j ] dp[i][j] dp[i][j] 时只依赖于左边、上边和左上角的位置, 因此可以通过一维数组优化空间复杂度。
  • 在计算过程中,我们只需要保存当前行和前一行的数据,从而将空间复杂度优化为 O ( n ) O(n) O(n)

4. 进一步优化:

  • 空间优化:使用一维数组来代替二维 d p d p dp 数组,通过滚动数组的方式减少空间量杂度,从 O ( m ∗ n ) O(m * n) O(mn) 降低到 O ( n ) O(n) O(n)

5. 小总结:

  • 动态规划方法通过逐步扩展矩阵中的 ′ 1 ′ ' 1 ' 1,计算出可以形成的最大正方形。该方法在时间昜杂度和空间昜杂度上表现较为优采。
  • 通过优化空间复杂度,可以将存储空间减少到 O ( n ) O(n) O(n) ,适用于大规模数据处理。动态规划问题的核心在于找到合理的递推关系,并根据问题特点优化计算过程。

以上就是最大正方形问题的基本思路。

代码实现

Python3代码实现

class Solution:
    def maximalSquare(self, matrix: list[list[str]]) -> int:
        if not matrix or not matrix[0]:
            return 0
        
        rows, cols = len(matrix), len(matrix[0])
        dp = [[0] * cols for _ in range(rows)]
        max_side = 0
        
        for i in range(rows):
            for j in range(cols):
                if matrix[i][j] == '1':
                    if i == 0 or j == 0:  # 边界条件,第一行或第一列
                        dp[i][j] = 1
                    else:
                        dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1
                    max_side = max(max_side, dp[i][j])
        
        # 最大面积
        return max_side * max_side

Python 代码解释

  • 初始化:首先初始化 d p dp dp 数组, d p [ i ] [ j ] dp[i][j] dp[i][j] 用于记录以 ( i , j ) (i, j) (i,j) 为右下角的最大正方形的边长。然后,遍历矩阵的每个元素。
  • 递推公式:当遇到矩阵元素为 1 时,计算 d p [ i ] [ j ] dp[i][j] dp[i][j] 的值。 d p [ i ] [ j ] dp[i][j] dp[i][j] 取决于左边、上边和左上角三个位置中最小的 d p dp dp 值,然后加 1 1 1
  • 边界条件:如果元素位于第一行或第一列,无法从上方或左方扩展正方形,因此直接将 d p [ i ] [ j ] dp[i][j] dp[i][j] 设为 1 1 1
  • 结果计算:每次计算时更新最大边长 max_side,最后返回最大边长的平方,即最大正方形的面积。

C++代码实现

class Solution {
public:
    int maximalSquare(vector<vector<char>>& matrix) {
        if (matrix.empty() || matrix[0].empty()) return 0;
        
        int rows = matrix.size();
        int cols = matrix[0].size();
        vector<vector<int>> dp(rows, vector<int>(cols, 0));
        int max_side = 0;
        
        for (int i = 0; i < rows; ++i) {
            for (int j = 0; j < cols; ++j) {
                if (matrix[i][j] == '1') {
                    if (i == 0 || j == 0) {  // 边界条件,第一行或第一列
                        dp[i][j] = 1;
                    } else {
                        dp[i][j] = min({dp[i-1][j], dp[i][j-1], dp[i-1][j-1]}) + 1;
                    }
                    max_side = max(max_side, dp[i][j]);
                }
            }
        }
        
        // 最大面积
        return max_side * max_side;
    }
};

C++ 代码解释

  • 初始化:首先创建 dp 数组,dp[i][j] 用于存储以 (i, j) 为右下角的最大正方形边长。初始化最大边长 max_side。
  • 动态规划递推:遍历矩阵中的每个位置。当 matrix[i][j] == '1' 时,通过递推公式计算 dp[i][j],更新 max_side。
  • 边界条件:对于第一行或第一列,直接将 dp[i][j] 设为1,因为无法通过左上、上方和左方扩展正方形。
  • 结果计算:最终返回最大边长的平方,即最大正方形的面积。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Albert_Lsk

今天又能喝柠檬茶啦

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

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

打赏作者

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

抵扣说明:

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

余额充值