单调栈解决【84. 柱状图中最大的矩形】【85. 最大矩形】【221. 最大正方形】
解题思路
我们维护一个这样单调栈:
- 大于栈顶元素,入栈
- 否则,弹出栈顶元素直到栈为空或者情形
1
成立
84. 柱状图中最大的矩形
首先来看84. 柱状图中最大的矩形,这样的栈为什么能解决最大矩形问题呢?
我们比较柱状图的高度,而在栈中存放的是柱状图的下标。
在弹栈的时候,计算出矩形的实际宽度 -- 索引比较,和实际高度 -- 高度比较:
- 假设当前单调栈为
[...,pre,top]
,栈顶索引为top
,当前索引为cur
- 矩形的高度就是栈顶索引的高度:
h = heights[top]
- 矩形的宽度为两边高度比
top
矮的柱体中间夹的部分 - 右边小于
top
的柱子是cur
- 左边小于
top
的柱子是栈顶索引的前一个索引pre
(进行pop
弹栈操作之后pre
变成下一个top
) - 综合
4
,5
,计算出矩形的宽度:w = cur-pre-1
(别忘了索引间距离-1) - 面积
s = w * h
遍历完heights
数组后,我们会得到一个单调递增栈,依次把所有索引弹出,比较即可求出最大矩形
// 伪代码
let stack=[],max=0
for (i of heights) {
if (curr > top) {
stack.push(curr) // 入栈
} else {
stack.pop()// 弹栈,计算矩形面积
}
}
// 继续弹栈,计算矩形面积
while(stack.length) {}
return max;
我们引入首尾2个辅助高度0,用来简化代码:
- 首位0用来计算首个柱子高度
- 末尾0可以驱动整个栈的弹出
let stack = [], max = 0;
heights = [0,...heights,0]; // 使用2个辅助高度作为边界
for (let j=0;j<heights.length;j++) {
while (stack.length>0 && heights[j] < heights[stack[stack.length-1]]) {
max = Math.max(
max,
// 高度 top = stack.pop()
// pop()操作之后,pre变成top,curr-pre-1
heights[stack.pop()] * (j-stack[stack.length-1]-1)
);
}
stack.push(j);
}
return max;
85. 最大矩形
如果你顺利解决了84题,那么恭喜你,85. 最大矩形只要稍作修改就能迎刃而解
- 矩阵的第
1
行,等效于高度为1
或0
的柱状图 - 第
N
行的矩阵,等价于第N-1
行的高度+1
或为0
的柱状图 - 遍历计算每一行的等效柱状图的最大矩形,比较得出结果
代码很简单,在84的基础上动态修改heights
数组的高度
let heights = new Array(matrix[i].length+1).fill(0), max = 0;
for (let i=0;i<matrix.length;i++) {
let stack = [];
for (let j=0;j<heights.length;j++) {
// 除了首尾辅助位置,更新heights数组高度
heights[j] = j>0&&j<=matrix[i].length&&'1'==matrix[i][j-1] ? heights[j]+1 : 0;
while (stack.length>0 && heights[j] < heights[stack[stack.length-1]]) {
max = Math.max(
max,
heights[stack.pop()] * (j-stack[stack.length-1]-1)
);
}
stack.push(j);
}
}
return max;
221. 最大正方形
最大矩形都求出来了,221. 最大正方形还不手到擒来?
- 遍历每一行矩阵,找出各行的矩形
- 找出最大的矩形的宽(短边)
代码与85
题几乎一模一样
/**
* @param {character[][]} matrix
* @return {number}
*/
var maximalSquare = function(matrix) {
if (matrix.length == 0) {
return 0;
}
let heights = new Array(matrix[0].length + 2).fill(0);
let max = 0;
for (let i=0;i<matrix.length;i++) {
let stack = [];
for (let j=0;j<heights.length;j++) {
heights[j] = i>0&&j<=matrix[i].length&&'1'==matrix[i][j-1] ? heights[j]+1 : 0;
while (stack.length>0 && heights[j] < heights[stack[stack.length-1]]) {
max = Math.max(
max,
Math.min(heights[stack.pop()], j-stack[stack.length-1]-1)// 矩形的宽
);
}
stack.push(j);
}
}
return max*max;
};