面试算法总结——单调栈的应用

前言

笔试面试中,单调栈用的也特别多,属于数据结构类的,在这里先总结下,免得以后忘了。这里只讲应用,不太涉及原理。

题目

1 矩形最大面积

首先来个经典例题:
求矩形的最大面积。((leetcode 85 maximal_Recetangle leetcode 84 largest_rectangle_in_histogram))
这个题目就是典型的单调栈。
运用单点栈的时候,首先想到的,应该是,建立一个单调递增还是单调递减的单调栈。这里维护一个单调递增的单调栈。每次遇到一个小于当前栈顶的数时(假设这个数为tn),把当前栈中所有比tn大的数,全部弹出栈。弹出栈的过程中,不要忘了,在弹出的过程中,不要忘了做相应的操作(如增加矩形的宽)。
以这组数据为例:
3 5 10 12 7 8 20
刚开始面积s = 0,首先把3入栈,然后5大于3,5入栈,10大于5,10入栈,12大于10,12入栈,7小于12,12出栈,同时s 更新为12;10比7大,10出栈,更新s为102 = 20。然后7入栈,这个时候,7的宽为3(10,12,7,三个数全部压成了7)。8入栈,20入栈。所有数遍历完了之后,栈中内容为3(1),5(1),7(3),8(1),20(1),s=20。20出栈,s=20,8出栈,s=max(s,82)=20,6出栈,s=max(s,6*5) = 30。。。这样操作下去。
贴出这个题目AC的代码:

    
    class R {
        public int x;
        public int y;

        public R(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }

    public int largestRectangleArea(int[] height) {
        if(height.length==0)
            return 0;
        
        Stack<R> stack = new Stack<>();
        //维护一个单调递zeng的单调栈
        int pos = 0;
        int max = 0;
        stack.add(new R(height[pos++], 1));
        while (pos < height.length) {
           \\ R tr = stack.peek();
            int len = 1;
            while (!stack.isEmpty() && (stack.peek().x > height[pos])) {
                len += stack.peek().y;
                max = Math.max((len - 1) * stack.peek().x, max);
                stack.pop();
            }
            stack.push(new R(height[pos++], len));
        }
        int len = 0;
        while (!stack.isEmpty()) {
            len += stack.peek().y;
            max = Math.max(max, len * stack.pop().x);
        }
     \\   System.out.print(max);
        return max;
    }

这是单调栈的一个典型应用,还有一个变形就是 给你一连串数字,要你找出一连串连续的数字,求出该区间段中所有数的和*该区间中最小值,也是单调栈的典型应用。
单调栈的时间复杂度,几乎可以认为为线性。
单点栈还有一类题目,经常出现,那就是 区间和的最小值

区间和的最小值

下面给一个参考链接,已经说的很详细了。
求数组中区间中最小数*区间所有数和的最大值
一维单调栈就说这么多吧,下面说说二维单调栈。

滑动窗口的最大值

滑动窗口的最大值,可以说是单调栈的经典运用了。
下面给出单调队列解题详解
额外提一句,这里有个很奇葩的想法,就是等分成n/k份,每份k的长度,left[i]代表到i的最大值,right[i]代表到i的最大值,详细解法可以看详细通俗的思路分析,多解法解法四

2 一维单调栈进阶——二维单调栈

先来一个二维单调栈的典型题目。
leetcode最大矩形面积
这里是二维单调栈的典型应用,遍历数组,每一层当做一个一维的单调栈去处理。关键在于得到单调栈的矩形,这个矩形由上面的矩形传递下来。如果当前a[i]为0,则这个矩形高度为0,如果a[i]为1,则矩形面积为a[i-1]+1。
AC代码如下:

    class R {
        int x;
        int y;
        public R(int x, int y) {
            this.x = x;
            this.y = y;
        }
    }
    int dp[][];
    public int maximalRectangle(char[][] matrix) {
        if (matrix.length == 0)
            return 0;
        int len1 = matrix.length;
        int len2 = matrix[0].length;
        dp = new int[len1][len2];
        for (int i = 0; i < len1; i++) {
            for (int j = 0; j < len2; j++) {
                dp[i][j] = 0;
            }
        }
        for (int i = 0; i < len2; i++) {
            dp[0][i] = matrix[0][i] == '1' ? 1 : 0;
        }
        Stack<R> stack = new Stack<>();
        int a[] = new int[len2];
        a[0] = matrix[0][0] == '1' ? 1 : 0;
        int max = a[0];
        for (int i = 1; i < len2; i++) {
            if (matrix[0][i] == '1') {
                a[i] = a[i - 1] + 1;
                max = Math.max(max, a[i]);
            } else {
                a[i] = 0;
            }
        }
        for (int i = 1; i < len1; i++) {
            for (int j = 0; j < len2; j++) {
                if (matrix[i][j] == '1') {
                    dp[i][j] = dp[i - 1][j] + 1;
                }
                int len = 1;
                while (!stack.isEmpty() && (stack.peek().x > dp[i][j])) {
                    len = len + stack.peek().y;
                    max = Math.max(max, (len - 1) * stack.peek().x);
                    stack.pop();
                }
                stack.push(new R(dp[i][j], len));
            }
            int slen = 0;
            while (!stack.isEmpty()) {
//                System.out.print(stack.peek().x + " " + stack.peek().y + " ");
                slen += stack.peek().y;
                max = Math.max(max, stack.peek().x * slen);
                stack.pop();
            }
//            System.out.println();

        }
        return max;
    }

这时复杂度就是在一维单调栈上面加一维。
这个题目还有一个相关变形,就是二维矩形的最大和。不过这个就完全不是单调栈的揭发了,二十动态规划,后面有时间再总结。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值