• 单调栈总结


    今天做周赛又碰到了单调栈的题目,之前没有做好总结,这次好好总结下

    1.基本思想

    单调栈求解的基本问题

    在一个线性数据结构中,为任意一个元素找左边和右边第一个比自己大/小的位置,要求O(n)的复杂度

    基本解法很容易想到O(n^2)的解法,关键是O(n)的解法,就需要借助单调栈了。单调栈的一大优势就是线性的时间复杂度,所有的元素只会进栈一次,而且一旦出栈后就不会再进来了。

    单调栈的性质

    1. 单调栈里的元素具有单调性:单调递减栈=>向栈生长的地方单调递减;单调递增栈=>向栈生长的地方单调递增

    2. 元素加入栈前,会在栈顶端把破坏栈单调性的元素都删除

    3. 使用单调栈可以找到元素向左遍历第一个比他小的元素,也可以找到元素向左遍历第一个比他大的元素。

    一般解题思路

    1. 栈中不存放值,而是存放下标;
    2. 看是需要找左边(右边)第一个小于某数的数,还是大于某数的数

    总结

    递减栈会剔除波谷,留下波峰;递增栈剔除波峰,留下波谷

    2.例题

    lc 42. Trapping Rain Water

    题意:

    Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.

    思路:

    对于每一个洼地,想要找到左起第一个比它小的数,则可以使用单调递减栈
    对于每个数,和栈顶元素比较,如果小于栈顶元素,则入栈;如果大于栈顶元素了,
    则入栈会破坏栈顶规则,那么就该出栈比较了,
    因为构成水洼至少需要两个点,那么现在栈顶的点是最低的挖槽,再下一个栈顶才是左边界,当前的点是右边界
    取左右两边的最低高度,减去挖槽的高度,再乘以左右边界的宽度,就得到了这个挖槽的蓄水体积

    代码:

    class Solution {
        public int trap(int[] height) {
            int res = 0;
            Stack<Integer> stack = new Stack<>();
            int i = 0;
            while (i < height.length) {
                if (stack.isEmpty() || height[i] < height[stack.peek()]) {
                    stack.push(i++);
                } else {
                    int bottom = stack.peek();
                    stack.pop();
                    if (stack.isEmpty()) continue;
                    int leftBound = stack.peek();
                    int rain = (Math.min(height[leftBound], height[i]) - height[bottom]) * (i - leftBound - 1);
                    res += rain;
                }
            }
            return res;
        } 
    }
    

    lc 84. Largest Rectangle in Histogram

    题意:

    Given n non-negative integers representing the histogram's bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.

    思路:

    单调递增栈
    应该先处理高板子,再处理低板子,最好高板子的左边都是比它低的,那么可以维护一个单调递增栈,
    然后每次遇到height[i] < stack.peek() 的话,说明该来计算面结了,那么就可以每次pop出栈顶元素,
    当前的矩形面积是栈顶元素的高度与栈顶元素与i的宽度构成的;
    (因为栈顶元素的下面的元素都比它小,也就是之前pop出的都比它大,那么当前到i之间的高度最低的就是栈顶元素的高度)

    代码:

    class Solution {
        public int largestRectangleArea(int[] heights) {
            List<Integer> nums = new ArrayList<>();
            for (int h : heights) nums.add(h);
            nums.add(0);
            Stack<Integer> stack = new Stack<>();
            int i = 0, res = 0;
            while (i < nums.size()) {
                if (stack.isEmpty() || nums.get(i) > nums.get(stack.peek())) {
                    stack.push(i++);
                } else {
                    int topIndex = stack.peek();
                    stack.pop();
                    res = Math.max(res, nums.get(topIndex) * (stack.isEmpty() ? i : i - stack.peek() - 1));
                }
            }
            return res;
        }
    }
    

    lc 962. Maximum Width Ramp

    题意:

    Given an array A of integers, a ramp is a tuple (i, j) for which i < j and A[
    i] <= A[j]. The width of such a ramp is j - i.
    Find the maximum width of a ramp in A. If one doesn't exist, return 0.

    思路:

    找到左边第一个比它小的数
    那么用递增栈还是递减栈呢?=>
    如果我们从右向左遍历每个数的话,希望找到左起第一个比该数小的数,也就是希望栈顶是第一个比这个数小的数,=>
    那么如果我们能保证栈顶右边的数,都比栈顶的数大,就是对于右边的数来说,栈顶的数就是第一个小于它的数了 =>
    可以维护一个递减栈

    代码:

    class Solution {
        public int maxWidthRamp(int[] A) {
            Stack<Integer> stack = new Stack<>();
            int n = A.length;
            for (int i = 0; i < n; i++) {
                if (stack.isEmpty() || A[stack.peek()] > A[i]) {
                    stack.push(i);
                }
            }
            int res = 0;
            for (int i = n - 1; i > 0; i--) {
                while (!stack.isEmpty() && A[i] >= A[stack.peek()]) {
                    res = Math.max(res, i - stack.pop());
                }
            }
            return res;
        }
    }
    

    总结

    能用单调栈的题目一般会有多种阶解法,比如双指针、hash map等,但是复杂度可能达不到O(n);有时候单调栈比较难想,先考虑下其他解法;
    单调栈的题,也有一定的套路,有时候单调递增递减栈不好确定,可以顺着题意想一想哪种能解;

  • 相关阅读:
    1.JavaScript面试
    input框限制只能输入正整数、字母、小数、汉字
    js清除浏览器缓存的几种方法
    document的createDocumentFragment()方法
    javascript画直线和画圆的方法(非HTML5的方法)
    input框限制只能输入正整数,逻辑与和或运算
    user-select : 保护版权内容的简单方案
    JQuery中$.ajax()方法参数详解
    字符串转化为json方法
    原型和闭包重点
  • 原文地址:https://www.cnblogs.com/shawshawwan/p/10166459.html
Copyright © 2020-2023  润新知