算法:【单调栈】

单调栈模板

常见模型:找出每个数左边离它最近的比它大/小的数

Deque<Integer> stack = new LinkedList<>();
for (int i = 0; i < n; i ++ ){
    while (!stack.isEmpty( && check(stack.peek(), i)) {
        // 业务逻辑
    }
    stack.push(i);
}

1.接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

示例 1:
图片说明
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。

class Solution {
    // 1.单调栈:维护一个栈底到栈顶单调递减的单调栈,这样每次弹出的数,小于当前数和下面的数,即满足可接雨水的部分
    public int trap(int[] height) {
        int ans = 0;
        Deque<Integer> stack = new LinkedList<>();
        int n = height.length;
        for(int i=0; i<n; i++){
            while(!stack.isEmpty() && height[i] > height[stack.peek()]){
                int top = stack.pop();
                if(stack.isEmpty())
                    break;
                int left = stack.peek();
                int curwidth = i-left-1;
                int curheight = Math.min(height[i], height[left]) - height[top];
                ans += curwidth * curheight;
            }
            stack.push(i);
        }
        return ans;
    }

    // 2.动态规划
    // public int trap(int[] height) {
    //     int ans = 0;
    //     int n = height.length;
    //     if(n == 0)
    //         return ans;
    //     int leftmax[] = new int[n];
    //     leftmax[0] = height[0];
    //     for(int i=1; i<n; i++){
    //         leftmax[i] = Math.max(leftmax[i-1], height[i]);
    //     }
    //     int[] rightmax = new int[n];
    //     rightmax[n-1] = height[n-1];
    //     for(int i=n-2; i>=0; i--){
    //         rightmax[i] = Math.max(rightmax[i+1], height[i]);
    //     }

    //     for(int i=0; i<n; i++){
    //         ans += Math.min(leftmax[i], rightmax[i]) - height[i];
    //     }

    //     return ans;
    // }
}

2.和至少为 K 的最短子数组

返回 A 的最短的非空连续子数组的长度,该子数组的和至少为 K 。
如果没有和至少为 K 的非空子数组,返回 -1 。

输入:A = [1], K = 1
输出:1

class Solution {
    // 前缀和 + 变种单调栈结构(栈底到栈顶单调递增)
    public int shortestSubarray(int[] nums, int k) {
        int[] sum = new int[nums.length+1]; // 前缀和数组
        for(int i=0; i<nums.length; i++){
            sum[i+1] = sum[i] + nums[i];
        }
        int res = -1;
        Deque<Integer> stack = new LinkedList<>();
        for(int i=0; i<=nums.length; i++){
            while(!stack.isEmpty() && sum[i] < sum[stack.peekLast()]){
                stack.removeLast();
            }
            while(!stack.isEmpty()){
                if(sum[i] - sum[stack.peekFirst()] >= k){
                    res = res == -1 ? i-stack.peekFirst() : Math.min(res,i-stack.peekFirst());
                    stack.removeFirst();
                }else{
                    break;
                }
            }
            if(sum[i] >= k){
                res = res==-1?i+1:Math.min(res,i+1);
            }
            stack.addLast(i);
        }
        return res;
    }
}

3. 二叉搜索树的后序遍历序列

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。

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

思路:

  • 后序遍历倒序: [ 根节点 | 右子树 | 左子树 ] 。类似 先序遍历的镜像 ,即先序遍历为 “根、左、右” 的顺序,而后序遍历的倒序为 “根、右、左” 顺序。
    图片说明
    图片说明
    class Solution {
      public boolean verifyPostorder(int[] postorder) {
          Stack<Integer> stack = new Stack<>();
          int root = Integer.MAX_VALUE;
          for(int i = postorder.length - 1; i >= 0; i--) {
              if(postorder[i] > root) return false;
              while(!stack.isEmpty() && stack.peek() > postorder[i])
                  root = stack.pop();
              stack.add(postorder[i]);
          }
          return true;
      }
    }

4.最大矩形

给定一个仅包含 0 和 1 、大小为 rows x cols 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。

示例1:
图片说明
输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]]
输出:6
解释:最大矩形如上图所示。

class Solution {
    public int maximalRectangle(char[][] matrix) {
        if(matrix.length == 0 || matrix[0].length == 0)
            return 0;
        int[] height = new int[matrix[0].length];
        int res = 0;
        for(int i=0; i<matrix.length; i++){
            for(int j=0; j<matrix[0].length; j++){
                height[j] = matrix[i][j] == '0' ? height[j] = 0 : height[j]+1;
            }
            res = Math.max(res, process(height));
        }
        return res;
    }

    // 单调栈
    public int process(int[] height){
        if(height == null || height.length == 0)
            return 0;
        int res = 0;
        Deque<Integer> stack = new LinkedList<>();
        for(int i=0; i<height.length; i++){
            while(!stack.isEmpty() && height[stack.peek()] >= height[i]){
                int j = stack.pop();
                int k = stack.isEmpty() ? -1 : stack.peek();
                int width = i-k-1;
                res = Math.max(res, width*height[j]);
            }
            stack.push(i);
        }
        int i = height.length;
        while(!stack.isEmpty()){
            int j = stack.pop();
            int k = stack.isEmpty() ? -1 : stack.peek();
            int width = i-k-1;
            res = Math.max(res, width*height[j]);
        }
        return res;
    }
}

5.下一个更大元素 II

给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。
输入: [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;
数字 2 找不到下一个更大的数;
第二个 1 的下一个最大的数需要循环搜索,结果也是 2。

注意事项
注意到只遍历一次序列是不够的,例如序列 [2,3,1],最后单调栈中将剩余 [3,1],其中元素 [1] 的下一个更大元素还是不知道的。

一个朴素的思想是,我们可以把这个循环数组「拉直」,即复制该序列的前 n-1 个元素拼接在原序列的后面。这样我们就可以将这个新序列当作普通序列,用上文的方法来处理。

而在本题中,我们不需要显性地将该循环数组「拉直」,而只需要在处理时对下标取模即可。

class Solution {
    // 单调栈
    public int[] nextGreaterElements(int[] nums) {
        Deque<Integer> stack = new LinkedList<>();
        int[] res = new int[nums.length];
        int n = nums.length;
        Arrays.fill(res, -1);
        for(int i=0; i<n*2-1; i++){
            while(!stack.isEmpty() && nums[i%n] > nums[stack.peek()]){
                int j =a stack.pop();
                res[j] = nums[i%n];
            }
            stack.push(i%n);
        }
        return res;
    }
}

6.132 模式

图片说明

class Solution {
    // 1.单调栈
    public boolean find132pattern(int[] nums) {
        if(nums.length < 3)
            return false;
        Deque<Integer> stack = new LinkedList<>();
        int k = -1;
        for(int i=nums.length-1; i>=0; i--){
            if(k>-1 && nums[k]>nums[i])
                return true;
            while(!stack.isEmpty() && nums[i] > nums[stack.peek()]){
                k = stack.pop();
            }
            stack.push(i);
        }
        return false;
    }
}
全部评论

相关推荐

点赞 评论 收藏
分享
饼子吃到撑:当我看到外企的时候,我就知道这大概率可能是真的
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务