• 单调栈解决【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;
    };
    
  • 相关阅读:
    JDBC之一:JDBC快速入门
    AdapterView及其子类之四:基于ListView及SimpleAdapter实现列表
    AdapterView及其子类之三:基于ListView及ArrayAdapter实现列表
    AdapterView及其子类之二:使用ListActivity及ArrayAdapter创建列表
    AdapterView及其子类之一:基本原理(ListView、ListActivity类型)
    Fragment之一:基本原理
    Loader之二:CursorLoader基本实例
    Intent 跳转Activity
    Android 第三课 构建简单的用户界面
    android第二课:运行你的应用
  • 原文地址:https://www.cnblogs.com/dapianzi/p/12853669.html
Copyright © 2020-2023  润新知