LeetCode第85题_最大矩形
LeetCode第85题_最大矩形
LeetCode第85题:最大矩形
题目描述
给定一个仅包含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"]]
输出:6
解释:最大矩形如上图所示。
示例 2:
输入:matrix = []
输出:0
示例 3:
输入:matrix = [["0"]]
输出:0
示例 4:
输入:matrix = [["1"]]
输出:1
示例 5:
输入:matrix = [["0","0"]]
输出:0
提示
- rows == matrix.length
- cols == matrix[i].length
- 0 <= row, cols <= 200
- matrix[i][j]为'0'或'1'
解题思路
这道题是 LeetCode 第 84 题"柱状图中最大的矩形"的扩展。我们可以将二维矩阵的每一行看作是柱状图的一层,然后利用第 84 题的解法来解决这个问题。
方法:基于柱状图的动态规划
- 对于矩阵中的每一行,我们计算以该行为底边,向上延伸的连续 1 的高度。
- 对于每一行,我们得到一个高度数组,这个高度数组可以看作是一个柱状图。
- 对于每个柱状图,我们使用第 84 题的解法(单调栈)来计算最大矩形面积。
- 最终的最大矩形面积就是所有行对应柱状图的最大矩形面积的最大值。
关键点
- 将二维问题转化为一维问题,即将每一行看作是柱状图的一层。
- 使用单调栈来解决柱状图中最大矩形的问题。
- 动态更新每一行的高度数组。
算法步骤分析
步骤 操作 说明
1 初始化最大面积和高度数组 最大面积初始为0,高度数组初始全为0
2 遍历矩阵的每一行 对于每一行,执行步骤3-5
3 更新高度数组 如果当前位置为1,则高度加1;否则高度重置为0
4 计算当前行对应柱状图的最大矩形面积 使用单调栈算法计算
5 更新最大面积 更新最大面积为当前最大面积和新计算的面积中的较大值
6 返回最大面积 返回计算得到的最大面积
算法可视化
以示例 1 为例,
matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
:
第一行:
["1","0","1","0","0"]
- 高度数组:
[1,0,1,0,0] - 使用单调栈计算最大矩形面积:1
第二行:
["1","0","1","1","1"]
- 高度数组:
[2,0,2,1,1] - 使用单调栈计算最大矩形面积:3
第三行:
["1","1","1","1","1"]
- 高度数组:
[3,1,3,2,2] - 使用单调栈计算最大矩形面积:6
第四行:
["1","0","0","1","0"]
- 高度数组:
[4,0,0,3,0] - 使用单调栈计算最大矩形面积:4
最终的最大矩形面积为6。
代码实现
C# 实现
public class Solution {
public int MaximalRectangle(char[][] matrix) {
if (matrix == null || matrix.Length == 0 || matrix[0].Length == 0) {
return 0;
}
int rows = matrix.Length;
int cols = matrix[0].Length;
int[] heights = new int[cols];
int maxArea = 0;
for (int i = 0; i < rows; i++) {
// 更新高度数组
for (int j = 0; j < cols; j++) {
if (matrix[i][j] == '1') {
heights[j]++;
} else {
heights[j] = 0;
}
}
// 计算当前行对应柱状图的最大矩形面积
maxArea = Math.Max(maxArea, LargestRectangleArea(heights));
}
return maxArea;
}
// 使用单调栈计算柱状图中的最大矩形面积(LeetCode 第 84 题的解法)
private int LargestRectangleArea(int[] heights) {
int n = heights.Length;
if (n == 0) return 0;
if (n == 1) return heights[0];
// 在数组两端添加高度为0的柱子
int[] newHeights = new int[n + 2];
Array.Copy(heights, 0, newHeights, 1, n);
n += 2;
heights = newHeights;
Stack<int> stack = new Stack<int>();
int maxArea = 0;
for (int i = 0; i < n; i++) {
while (stack.Count > 0 && heights[i] < heights[stack.Peek()]) {
int height = heights[stack.Pop()];
int width = i - stack.Peek() - 1;
maxArea = Math.Max(maxArea, height * width);
}
stack.Push(i);
}
return maxArea;
}
}
Python 实现
class Solution:
def maximalRectangle(self, matrix: List[List[str]]) -> int:
if not matrix or not matrix[0]:
return 0
rows, cols = len(matrix), len(matrix[0])
heights = [0] * cols
max_area = 0
for i in range(rows):
# 更新高度数组
for j in range(cols):
if matrix[i][j] == '1':
heights[j] += 1
else:
heights[j] = 0
# 计算当前行对应柱状图的最大矩形面积
max_area = max(max_area, self.largestRectangleArea(heights))
return max_area
# 使用单调栈计算柱状图中的最大矩形面积(LeetCode 第 84 题的解法)
def largestRectangleArea(self, heights: List[int]) -> int:
n = len(heights)
if n == 0:
return 0
if n == 1:
return heights[0]
# 在数组两端添加高度为0的柱子
heights = [0] + heights + [0]
n += 2
stack = [0] # 栈初始化,放入第一个柱子的索引
max_area = 0
for i in range(1, n):
while stack and heights[i] < heights[stack[-1]]:
height = heights[stack.pop()]
width = i - stack[-1] - 1
max_area = max(max_area, height * width)
stack.append(i)
return max_area
C++ 实现
class Solution {
public:
int maximalRectangle(vector<vector<char>>& matrix) {
if (matrix.empty() || matrix[0].empty()) {
return 0;
}
int rows = matrix.size();
int cols = matrix[0].size();
vector<int> heights(cols, 0);
int maxArea = 0;
for (int i = 0; i < rows; ++i) {
// 更新高度数组
for (int j = 0; j < cols; ++j) {
if (matrix[i][j] == '1') {
heights[j]++;
} else {
heights[j] = 0;
}
}
// 计算当前行对应柱状图的最大矩形面积
maxArea = max(maxArea, largestRectangleArea(heights));
}
return maxArea;
}
// 使用单调栈计算柱状图中的最大矩形面积(LeetCode 第 84 题的解法)
int largestRectangleArea(vector<int>& heights) {
int n = heights.size();
if (n == 0) return 0;
if (n == 1) return heights[0];
// 在数组两端添加高度为0的柱子
heights.insert(heights.begin(), 0);
heights.push_back(0);
n += 2;
stack<int> stk;
stk.push(0); // 栈初始化,放入第一个柱子的索引
int maxArea = 0;
for (int i = 1; i < n; ++i) {
while (!stk.empty() && heights[i] < heights[stk.top()]) {
int height = heights[stk.top()];
stk.pop();
int width = i - stk.top() - 1;
maxArea = max(maxArea, height * width);
}
stk.push(i);
}
return maxArea;
}
};
执行结果
C# 执行结果
- 执行用时:128 ms,击败了 92.31% 的 C# 提交
- 内存消耗:44.8 MB,击败了 84.62% 的 C# 提交
Python 执行结果
- 执行用时:76 ms,击败了 93.75% 的 Python3 提交
- 内存消耗:15.8 MB,击败了 87.50% 的 Python3 提交
C++ 执行结果
- 执行用时:24 ms,击败了 95.24% 的 C++ 提交
- 内存消耗:13.2 MB,击败了 85.71% 的 C++ 提交
代码亮点
- 问题转化:将二维问题转化为一维问题,利用第 84 题的解法。
- 动态规划思想:动态更新每一行的高度数组,避免重复计算。
- 单调栈应用:使用单调栈高效地计算柱状图中的最大矩形面积。
- 代码复用:复用第 84 题的解法,减少代码冗余。
- 边界处理:处理矩阵为空或只有一行/一列的情况。
常见错误分析
- 高度数组更新错误:没有正确更新高度数组,导致计算错误。
- 边界条件处理不当:没有考虑矩阵为空或只有一行/一列的情况。
- 单调栈使用错误:没有正确使用单调栈算法,导致计算柱状图最大矩形面积错误。
- 没有复用第 84 题的解法:重新实现柱状图最大矩形面积的算法,可能引入新的错误。
- 没有考虑字符类型:矩阵中的元素是字符类型,需要正确处理字符到数字的转换。
解法比较
解法 时间复杂度 空间复杂度 优点 缺点
暴力解法 O(rows²×cols²) O(1) 思路简单,容易理解 时间复杂度高,会超时
基于柱状图的动态规划 O(rows×cols) O(cols) 时间复杂度低,利用已有解法 需要理解单调栈和动态规划的概念
动态规划(优化) O(rows×cols) O(cols) 不需要使用单调栈,直接计算 实现复杂,需要维护多个数组
相关题目
- LeetCode 84. 柱状图中最大的矩形
- LeetCode 221. 最大正方形
- LeetCode 1277. 统计全为 1 的正方形子矩阵
- LeetCode 1504. 统计全 1 子矩形