单调栈解决【84. 柱状图中最大的矩形】【85. 最大矩形】【221. 最大正方形】

解题思路

我们维护一个这样单调栈:

  • 大于栈顶元素,入栈
  • 否则,弹出栈顶元素直到栈为空或者情形1成立

84. 柱状图中最大的矩形

首先来看84. 柱状图中最大的矩形,这样的栈为什么能解决最大矩形问题呢?
我们比较柱状图的高度,而在栈中存放的是柱状图的下标。
在弹栈的时候,计算出矩形的实际宽度 -- 索引比较,和实际高度 -- 高度比较:

  1. 假设当前单调栈为[...,pre,top],栈顶索引为top,当前索引为cur
  2. 矩形的高度就是栈顶索引的高度: h = heights[top]
  3. 矩形的宽度为两边高度比top矮的柱体中间夹的部分
  4. 右边小于top的柱子是cur
  5. 左边小于top的柱子是栈顶索引的前一个索引pre(进行pop弹栈操作之后pre变成下一个top)
  6. 综合4,5,计算出矩形的宽度: w = cur-pre-1 (别忘了索引间距离-1)
  7. 面积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行,等效于高度为10的柱状图
  • 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;
};
posted @ 2020-05-08 23:07  真理君的宿敌  阅读(239)  评论(0编辑  收藏  举报