leetcode最大矩形_LeetCode葵花宝典-单调栈

葵花宝典第一针:单调栈

  • 单调栈(Monotonic Stack)介绍

单调栈就是一个栈,使得每次新元素入栈后,栈内元素单调递增或单调递减。

单调栈模板:

    public int[] NextGreaterElement (int[] nums) {
        Stack<Integer> stack = new Stack<>();
        int[] res = new int[nums.length];
        Arrays.fill(res, -1);
        for (int i = 0; i < nums.length; i++) {
            int value = nums[i];
            //这个while很关键,因为有可能不止一个数可以被计算出来
            while (!stack.empty() && value > nums[stack.peek()]) {
                //根据题意执行计算
                stack.pop();
            }
            stack.push(i);
        }
        return res;
    }

单调栈用于处理一种叫做Next Greater Element的问题:

给一个数组,返回一个等长的数组,对应索引存储着下一个更大元素,如果没有更大的元素,就存-1。

例如:

input:5,3,1,2,4

return:-1,3,1,1,-1

暴力解法就是对每个元素扫描,找到第一个更大的元素,时间复杂度是O(n^2)。

单调栈解法代码:

    public static int[] nextGreaterElement (int[] nums) {
        Stack<Integer> stack = new Stack<>();
        int[] res = new int[nums.length];
        Arrays.fill(res, -1);
        for (int i = 0; i < nums.length; i++) {
            int value = nums[i];
            while (!stack.empty() && value > nums[stack.peek()]) {
                res[stack.peek()] = i - stack.peek();
                stack.pop();
            }
            stack.push(i);
        }
        return res;
    }

我们维护这样一个单调递减的栈stack,内部存的是原数组的索引index。当遇到比当前栈顶所对应的数(nums[stack.peek()])大的数的时候,依次弹出栈内所有比这个大数小的栈内元素,并更新它们在返回数组中对应位置的值。因为栈的单调性, 当我们栈内元素所对应的的数比push的元素大时,栈内所有元素都比push的元素大。每个元素出栈时,表明遇到了自己的Next Greater Element,我们也就要更新return数组中对应位置的值。如果有元素一直不出栈,说明不存在Next Greater Element,也就不用更新return数组了。时间复杂度是O(n)。

再看一道难的题:(题解参考:

力扣​leetcode-cn.com

3021a0a92f9bdf96639cc588d8c73905.png

20b7d94e61b3b41d9464860d5cecedc9.png
    public int largestRectangleArea(int[] heights) {
        if (heights == null || heights.length == 0) return 0;
        if (heights.length == 1) return heights[0];
        int len = heights.length;
        Stack<Integer> stack = new Stack<>();
        stack.push(-1);
        int sum = 0;
        for (int i = 0; i < len; i++) {
            int value = heights[i];
            while (stack.peek() != -1 && value < heights[stack.peek()]) {
                int high = heights[stack.peek()];
                stack.pop();
                int tmp = high * (i - stack.peek() - 1);
                sum = Math.max(tmp, sum);
            }
            stack.push(i);
        }

        while(stack.peek() != -1) {
            int tmp = stack.peek();
            stack.pop();
            if(stack.peek() != -1) {
                sum = Math.max(sum, heights[tmp] * (heights.length - stack.peek() - 1));
            } else {
                sum = Math.max(sum, heights[tmp] * heights.length);
            }
        }

        return sum;
    }

用栈以空间换时间,时间复杂度是O(N^2),空间复杂度是O(1)

栈内记录什么信息呢?记录高度是不是可以呢?其实是不够的,因为计算矩形还需要计算宽度,宽度是由下标确定的,记录了下标对应的高度可以从输入数组中得出,因此应该记录的是下标。

所以,在遍历的时候要记录的信息就是遍历到的柱形的下标,它一左一右的两个柱形的下标的差就是这个面积最大的矩形对应的最大宽度。只要是遇到了当前柱形的高度比它上一个柱形的高度严格小的时候,一定可以确定它之前的某些柱形的最大宽度,并且确定的柱形宽度的顺序是从右边向左边。为了确定这个最大矩形的左边界,我们还要找到第一个严格小于它的高度的矩形,向左回退的时候,其实就可以当中间这些矩形不存在一样。

我们缓存数据的时候,是从左向右缓存的,我们计算一个结果的顺序是从右向左的,并且计算完就不需要了,符合后进先出的特点。所以缓存的数据结构用栈。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值