文章更新:2021年10月23日13:42:02
在一个由 ‘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
提示:
m == matrix.length
n == matrix[i].length
1 <= m, n <= 300
matrix[i][j] 为 ‘0’ 或 ‘1’
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximal-square
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
这道题题目的动态规划味道还是比较明显的。有关动态规划的思路总结,可以参看下方的博客,里面也有我目前做的其他动态规划题目:
参考:【算法-LeetCode】53. 最大子序和(动态规划初体验)_赖念安的博客-CSDN博客
本题大致思路如下:
①确定 dp[i][j]
的含义。
dp[i][j]
是指在 matrix
中以元素 [i][j]
为右下角元素的正方形的边长。
②确定状态转移方程。
遍历 matrix
:
'1'
,那么就在 dp
数组中对应位置处取当前元素的左上方(即 dp[i-1][j-1]
)、正上方(即 dp[i-1][j]
)、正左方(即 dp[i][j-1]
)这三个元素中的最小值,并将该最小值加 1
作为当前 dp[i][j]
的值。'0'
,那么就取当前 dp[i][j]
的值为 0
。③完成 dp
数组的初始化。
由上面的状态转移方程可知,需要对 dp
数组的第一行和第一列做初始化。按理来说应该对 dp
数组的第一行和第一列单独求值,但是为了省去这些单独的操作,我们可以给 dp
数组分别加上一个辅助行和辅助列。并且将这些辅助行和列的值全部填充为 0
。这样就可以统一逻辑。
接下来就是由上到下,由前到后地遍历 martix
并填充 dp
数组了。
详解请看下方注释:
/**
* @param {character[][]} matrix
* @return {number}
*/
var maximalSquare = function(matrix) {
// width用于存储当前矩阵中的最大正方形的边长,初始值应为0
let width = 0;
// 创建二维dp数组,注意其长宽均比matrix大1,且其元素的值均被初始化为0,
// 这就省去了单独初始化首行和首列的操作
let dp = Array.from({length: matrix.length+1}).map(
() => Array.from({length: matrix[0].length+1}).fill(0)
);
// 开始遍历matrix,或者说开始填充dp数组,注意,i和j的值都是从1开始的
for(let i = 1; i <= matrix.length; i++) {
for(let j = 1; j <= matrix[0].length; j++) {
// 如果当前遍历的元素的值为 “1”,那么就在dp数组中的对应位置的左上方、正上方、正左方
// 这三个元素中选取最小值,该最小值加1即为当前dp[i][j]的值
if(matrix[i-1][j-1] === "1") {
dp[i][j] = Math.min(dp[i-1][j-1], dp[i-1][j], dp[i][j-1]) + 1;
// 更新dp[i][j]后,还要更新width的值为最长
width = Math.max(dp[i][j], width);
}
}
}
// 最后返回最大正方形的面积
return width * width;
};
提交记录
75 / 75 个通过测试用例
状态:通过
执行用时:88 ms, 在所有 JavaScript 提交中击败了55.44%的用户
内存消耗:41.2 MB, 在所有 JavaScript 提交中击败了41.89%的用户
时间:2021/10/23 13:47
因为本题中 dp[i][j]
的取值都是来自其左方和上方区域,所以可以用上我之前总结的空间优化思路来节省内存消耗,详细解释可参看下方博客:
参考:【算法-LeetCode】1143. 最长公共子序列(动态规划;滚动数组;通用的空间优化)_赖念安的博客-CSDN博客
本题也是一样的套路,将二维 dp
数组的宽度固定为 2
,而把其长度设置为 matrix[0]
的长度。dp[1]
就是被滚动的那个。
/**
* @param {character[][]} matrix
* @return {number}
*/
var maximalSquare = function(matrix) {
let width = 0;
let dp = Array.from({length: 2}).map(
() => Array.from({length: matrix[0].length+1}).fill(0)
);
for(let i = 1; i <= matrix.length; i++) {
for(let j = 1; j <= matrix[0].length; j++) {
if(matrix[i-1][j-1] === "1") {
dp[1][j] = Math.min(dp[0][j-1], dp[0][j], dp[1][j-1]) + 1;
width = Math.max(dp[1][j], width);
}
}
// 其他的逻辑都和上面的差不多,关键是下面的滚动操作,将dp数组第二行的数据复制到第一行,
// 同时将第二行的数据重新初始化为0(注意重置为0这一步不能省略,详看下方【补充1】)
dp[0] = [...dp[1]];
dp[1].fill(0);
}
return width * width;
};
提交记录
75 / 75 个通过测试用例
状态:通过
执行用时:84 ms, 在所有 JavaScript 提交中击败了70.23%的用户
内存消耗:40.3 MB, 在所有 JavaScript 提交中击败了93.43%的用户
时间:2021/10/23 14:38
可以看到,这种解法的空间表现还是有比较大的提升的。
【补充1】
如果没有将 dp
数组第二行重置为 0
,那么就会影响后续的 dp
数组元素的填充:
因为在状态转移方程中没有对 matrix[i-1][j-1] === "0"
的情况做判断,按理来说,应该要将该情况下的 dp[i][j]
设置为 0
,但是我们没有这样设置,此时如果不进行重置操作,那么就会对后续的 dp[i][j]
取值造成影响。
更新:2021年7月29日18:43:21
因为我考虑到著作权归属问题,所以【官方题解】部分我不再粘贴具体的代码了,可到下方的链接中查看。
更新:2021年10月23日13:51:50
参考:最大正方形 - 最大正方形 - 力扣(LeetCode)
【更新结束】
更新:2021年10月23日13:52:33
参考:【算法-LeetCode】53. 最大子序和(动态规划初体验)_赖念安的博客-CSDN博客
参考:【JavaScript基础-二维数组】JavaScript修改二维数组的某个元素时,其上下元素也受到影响_赖念安的博客-CSDN博客
更新:2021年10月23日14:43:58
参考:【算法-LeetCode】1143. 最长公共子序列(动态规划;滚动数组;通用的空间优化)_赖念安的博客-CSDN博客