栈&分治 - [84. 柱状图中最大矩形]

栈&分治 84. 柱状图中最大矩形

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GTwyvAyf-1578897821183)(en-resource://database/8703:1)]

以上是柱状图的示例,其中每个柱子的宽度为 1,给定的高度为 2,1,5,6,2,3]

在这里插入图片描述

图中阴影部分为所能勾勒出的最大矩形面积,其面积为 10 个单位。

示例:

输入: [2,1,5,6,2,3]
输出: 10

一. 实现思路

1. 暴力解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mxcqtOBd-1578897821185)(en-resource://database/8562:1)]

循环每个元素,查询这个元素前面的数或者后面的数是否比他大:getBeforegetAfter
如果大,接着往下找,如果小就说明往前往后无法在拼接成完整的矩形
这个解法比较好理解,但是效率非常低

2. 单调栈

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NxFMiD0w-1578897821186)(en-resource://database/8701:1)]

在这种方法中,我们维护一个栈。

  1. 把 -1 放进栈的顶部来表示开始。
  2. 初始化时,按照从左到右的顺序,我们不断将柱子的序号放进栈中,直到遇到相邻柱子呈下降关系,也就是 a[i-1] > a[i]
  3. 然后将栈中的序号弹出,直到遇到 stack[j] 满足 a[stack[j]]<=a[i]
  4. 每次我们弹出下标时,我们用弹出元素作为高形成的最大面积矩形的宽是当前元素stack[top-1] 之间的那些柱子。也就是当我们弹出 stack[top] 时,记当前元素在原数组中的下标为 i ,当前弹出元素为高的最大矩形面积为:(i−stack[top−1]−1)×a[stack[top]]
  5. 更进一步,当我们到达数组的尾部时,我们将栈中剩余元素全部弹出栈。在弹出每一个元素是,我们用下面的式子来求面积: (stack[top]−stack[top−1])×a[stack[top]] ,其中,stack[top] 表示刚刚被弹出的元素。因此,我们可以通过每次比较新计算的矩形面积来获得最大的矩形面积。
3. 判断极端情况的分治

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QGAQKEbu-1578897821187)(en-resource://database/8699:1)]

不断拆分找最小
极端情况:判断了递增和递减两种情况

二.代码实现

1. 暴力解
class Solution {

    public int largestRectangleArea(int[] heights) {

         if (heights.length==0){
            return 0;
        }

        int[] xl = new int[heights.length];

        List<Integer> integers = Arrays.stream(heights).boxed().collect(Collectors.toList());
        for (int i = 0; i < integers.size(); i++) {
            if (i!=0){
               if (integers.get(i)==integers.get(i-1)){
                   continue;
               }
            }
            xl[i] = getBefore(integers,i,integers.get(i),integers.get(i))+getLast(integers,i,integers.get(i),0,integers.size());
        }
        Arrays.sort(xl);
        return xl[heights.length-1];
    }

    public int getBefore(List<Integer> integers, int index, int current ,int beforeTotal){
        if (index==0){
            return  beforeTotal;
        }
        if(integers.get(index - 1)<current){
            return  beforeTotal;
        }else {
            beforeTotal+=current;
            index--;
            beforeTotal = getBefore(integers,index,current,beforeTotal);
            return beforeTotal;
        }
    }

    public int getLast(List<Integer> integers, int index, int current ,int lastTotal,int length){
        if (index==length-1){
            return  lastTotal;
        }
        if(integers.get(index + 1)<current){
            return  lastTotal;
        }else {
            lastTotal+=current;
            index++;
            lastTotal = getLast(integers,index,current,lastTotal,length);
            return lastTotal;
        }
    }
}
2. 单调栈
public class Solution {
    public int largestRectangleArea(int[] heights) {
        Stack < Integer > stack = new Stack < > ();
        stack.push(-1);
        int maxarea = 0;
        for (int i = 0; i < heights.length; ++i) {
            while (stack.peek() != -1 && heights[stack.peek()] >= heights[i])
                maxarea = Math.max(maxarea, heights[stack.pop()] * (i - stack.peek() - 1));
            stack.push(i);
        }
        while (stack.peek() != -1)
            maxarea = Math.max(maxarea, heights[stack.pop()] * (heights.length - stack.peek() -1));
        return maxarea;
    }
}
3. 判断极端情况的分治
class Solution {
    public int largestRectangleArea(int[] heights) {
        return largest(heights, 0, heights.length-1);
    }
     
    public int largest(int[] heights, int start, int end) {
        if (start > end) {
            return 0;
        }
 
        // 数组是递增的
        boolean up = true;
        boolean dowm = true;
 
        // 先找最矮的
        int minIndex = start;
        for (int i = start + 1; i <= end; i++) {
            if (heights[i] < heights[i - 1]) {
                // 不是递增
                up = false;
            }
            if (heights[i] > heights[i - 1]) {
                // 不是递减
                dowm = false;
            }
            if (heights[i] < heights[minIndex]) {
                minIndex = i;
            }
        }
 
        // 极端情况,递增或递减直接求面积
        if (up) {
            int maxArea = 0;
            for (int i = start; i <= end; i++) {
                maxArea = Math.max(maxArea, heights[i] * (end - i + 1));
            }
            return maxArea;
        }
 
        if (dowm) {
            int maxArea = -1;
            for (int i = end; i >= start; i--) {
                maxArea = Math.max(maxArea, (i - start + 1) * heights[i]);
            }
            return maxArea;
        }
 
        // 当前层最大面积
        int currArea = heights[minIndex] * (end - start + 1);
        // 左边最大面积
        int leftArea = largest(heights, start, minIndex - 1);
        // 右边最大面积
        int rightArea = largest(heights, minIndex + 1, end);
        // 左右两边的最大面积
        int currMaxArea = Math.max(leftArea, rightArea);
 
        return Math.max(currArea, currMaxArea);
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值