今天刚做了两道Leetcode上难度为Hard的难题,使用到了单调栈的思想,这里将思路总结一下,便于后续复习。
这两道题分别是:
1.题目描述
1.1 Leetcode84. 柱状图中最大的矩形
给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
求在该柱状图中,能够勾勒出来的矩形的最大面积。
示例1:
输入:heights = [2,1,5,6,2,3] 输出:10 解释:最大的矩形为图中红色区域,面积为 10
1.2 Leetcode 85. 最大矩形
给定一个仅包含 0
和 1
、大小为 rows x cols
的二维二进制矩阵,找出只包含 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.思路分析
对于84题,可以计算每一个位置 i 处可以组成的矩形面积,遍历求最大值即可。
为了计算矩形面积,需要知道柱子的高和宽,高已经知道了,即heights[ i ],而宽可以通过计算 i 左右两边的边界位置来计算。左右两边边界的位置表示为:以 i 为中心,向左向右遍历到的第一个小于heights[ i ]的位置,可以通过单调栈快速计算。
对于85题,可以将图片逆时针旋转90°,看成是以列为单位一列一列的柱状图,和84题同理,计算每一列柱状图的最大矩形面积,然后求全局最大值即可。
进一步,要想计算柱状图面积,就需要知道柱子的高和宽:
- 计算高:用二维数组left[ i ][ j ]表示(i, j)位置左侧格子中1的个数(包括(i, j))。
- 计算宽:用单调栈计算左右边界,同84题。
3.解答
3.1 Leetcode84. 柱状图中最大的矩形
class Solution {
//单调栈
public int largestRectangleArea(int[] heights) {
int n = heights.length;
//分别存储当前位置左侧和右侧最近的边界(小于当前值的位置)
int[] left = new int[n];
int[] right = new int[n];
//使用单调栈查找每个位置的左右边界
Deque<Integer> stack = new ArrayDeque<>();
//从左向右遍历
for(int i = 0; i < n; i++){
while(!stack.isEmpty() && heights[i] <= heights[stack.peek()]){
stack.pop();
}
left[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(i);
}
stack.clear();
//从右向左遍历
for(int j = n - 1; j >= 0; j--){
while(!stack.isEmpty() && heights[j] <= heights[stack.peek()]){
stack.pop();
}
right[j] = stack.isEmpty() ? n : stack.peek();
stack.push(j);
}
//遍历计算以当前位置为中心的矩形面积,找出最大值
int res = 0;
for(int k = 0; k < n; k++){
res = Math.max(res, (right[k] - left[k] - 1) * heights[k]);
}
return res;
}
}
复杂度分析
-
时间复杂度:O(N)。
-
空间复杂度:O(N)。
3.2 Leetcode 85. 最大矩形
class Solution {
//单调栈
public int maximalRectangle(char[][] matrix) {
int m = matrix.length;
if(m == 0){
return 0;
}
int n = matrix[0].length;
//存储位置(i,j)左边的格子中1的个数
int[][] left = new int[m][n];
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
if(matrix[i][j] == '1'){
left[i][j] = (j == 0 ? 1 : left[i][j - 1] + 1);
}
}
}
//以列为单位进行遍历计算每一个矩形的面积
int res = 0;
for(int j = 0; j < n; j++){
//使用单调栈计算每一行矩形的长度(侧着看就是柱子的高度)
Deque<Integer> stack = new ArrayDeque<>();
//从上到下遍历
int[] up = new int[m];
for(int i = 0; i < m; i++){
while(!stack.isEmpty() && left[i][j] <= left[stack.peek()][j]){
stack.pop();
}
up[i] = stack.isEmpty() ? -1 : stack.peek();
stack.push(i);
}
stack.clear();
//从下到上
int[] down = new int[m];
for(int i = m - 1; i >= 0; i--){
while(!stack.isEmpty() && left[i][j] <= left[stack.peek()][j]){
stack.pop();
}
down[i] = stack.isEmpty() ? m : stack.peek();
stack.push(i);
}
//遍历计算每个矩形的面积
for(int k = 0; k < m; k++){
int high = down[k] - up[k] - 1;
int width = left[k][j];
res = Math.max(res, high * width);
}
}
return res;
}
}
复杂度分析
-
时间复杂度:O(mn)。
-
空间复杂度:O(mn)。