✿-10例题 栈、哨兵--柱状图中最大的矩形-84

Posted on 2020-11-10 20:21  MissRong  阅读(96)  评论(0)    收藏  举报

✿-10例题 栈、哨兵--柱状图中最大的矩形-84

一、题目

 二、Java代码

package shuJuJieGou.Zhan;

import java.util.ArrayDeque;
import java.util.Deque;

/**
 * @Author : ASUS and xinrong
 * @Version : 2020/11/9 & 1.0
 * 栈、哨兵
 * 柱状图中最大的矩形-84
 *
 * 给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。
 * 求在该柱状图中,能够勾勒出来的矩形的最大面积。
 *
 * 思考:
 * 这道题和盛水最多的容器很像,
 * 不同的是盛水问题只关注两端的柱子高度即可,而勾勒出矩形问题是要确保中间无悬空,这使问题加大了难度。
 * 1)审题:能够勾勒出的矩形height取决于最短的柱子的高(注意勾勒矩形不能有镂空),宽则取决于柱子的个数。
 * 2)解法:
 * (1)暴力破解:枚举出基于各个柱子能形成的最大矩形面积,两边延伸探测左右边界
 * (2)栈(单调栈):主要的思想还是和枚举的相似,都是要得到每个柱子的左右边界。
 *                  维护一个栈(栈底到栈顶,元素升序排列),栈内存的是位置信息
 *                  即将入栈的元素如果比栈顶元素大,入栈 。因为栈顶元素的右边界还可以再延伸。
 *                  即将入栈的元素如果比栈顶元素小,出栈 。因为栈顶元素的右边界到此确定了,
 *                                                   左边界即为出栈后的栈顶元素。
 */
public class LargestRectangleInHistogram {

    /**
     * 法一:单调栈 (时间复杂度:O(n)-输入数组的各个元素入栈、出栈各一次;空间复杂度:O(n)-栈的空间最多为n)
     * @param heights
     * @return
     */
    public int largestRectangleArea(int[] heights) {
        //特殊情况判断
        if (heights == null) {
            return 0;
        }
        //在柱体数组的头和尾加两个高度为0的柱体
        int[] tmp = new int[heights.length + 2];
        //    Object src : 原数组
        //    int srcPos : 从元数据的起始位置开始
        //  Object dest : 目标数组
        //  int destPos : 目标数组的开始起始位置
        //  int length  : 要copy的数组的长度
        System.arraycopy(heights, 0, tmp, 1, heights.length);
        //栈内,存的是柱体的位置
        Deque<Integer> deque = new ArrayDeque<>();
        //因为下面的while循环里的非空判断仅在最开始栈空的情况下有用,所以可用哨兵省略它
        deque.addFirst(0);
        int area = 0;
        //开始遍历每个柱子的左右边界
        for (int i = 0; i < tmp.length; i++) {
            //对于栈中柱体来说,栈中的下一个柱体的位置就代表它的左边界
            //因此,以栈顶柱体为高的矩形的左右边宽度边界就确定了,接下来要计算面积。
            while (tmp[i]<tmp[deque.peekFirst()]) {
                int h = tmp[deque.pollFirst()];
                //出栈就意味着这个柱子的左右边界已确定,可以求出以它为基础能够勾勒出来的矩形的最大面积
                area = Math.max(area, (i - deque.peekFirst() - 1) * h);
            }
            deque.addFirst(i);
        }
        return area;
    }
    /**
     * 法二:暴力破解法-枚举
     * @param heights
     * @return
     * 复杂度分析:
     * 时间复杂度:O(N^2),这里 N 是输入数组的长度。
     *   遍历每个柱子为O(n);每个柱子左右探测其边界为O(n)--最糟糕情况:可能全都需要探测一下
     * 空间复杂度:O(1)。
     */
    public int largestRectangleArea2(int[] heights) {
        //特殊情况:
        if (heights == null) {
            return 0;
        }
        //勾勒出来的矩形的最大面积
        int max = 0, length = heights.length;
        for (int i = 0; i <  length ; i++) {
            int height = heights[i];
            //左右逐渐探测
            int left = i;
            while (left > 0 && heights[left-1] >= height) {
                left--;
            }
            int right = i;
            while (right < length - 1 && heights[right+1] >= height) {
                right++;
            }
            max = Math.max(heights[i] * (right - left + 1), max);
        }
        return max;
    }
    public static void main(String[] args) {
        LargestRectangleInHistogram largestRectangleInHistogram = new LargestRectangleInHistogram();
        System.out.println(largestRectangleInHistogram.largestRectangleArea(new int[]{2,1,5,6,2,3}));
        System.out.println(largestRectangleInHistogram.largestRectangleArea(new int[]{1}));
    }
}

三、帮助理解

 while (tmp[i]<tmp[deque.peekFirst()]) {
                int h = tmp[deque.pollFirst()];
                //出栈就意味着这个柱子的左右边界已确定,可以求出以它为基础能够勾勒出来的矩形的最大面积
                area = Math.max(area, (i - deque.peekFirst() - 1) * h);
                //问题-1:怎么就确定出栈的元素左侧就没有大于等于它的元素?
                //  因为:入栈的时候就是只进入比栈顶元素大或等于的元素
                //问题-2:对于中间小的柱子,怎么确定?
                //  中间小的,可对栈内比他大的元素进行出栈操作,之后栈内它前面的元素一定比他小,
                //  而且他俩之间的位置差再-1就是之前大于等于这个矮柱子的元素(可以一起勾勒了矩形)个数
                //问题-3:为什么要-1呢?
                //  右边界所对应的元素小于左侧柱子大小,因此减去它
            }

 图解(程序加哨兵前)

 

 

 

 

 

 

博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3