046、单调栈


minds

  • 每天进步一点点就好了;
  • 稳一点,细心一点;

提示:以下是本篇文章正文内容

一、单调栈是什么?

可能单调栈的概念比较难形容,直接用题目来表达吧。

二、题目

1.每日温度

力扣:739. 每日温度

思路:

  • 从最后一天往前进行遍历,利用一个栈;
  • 只有当比较的当前的温度大于栈顶中的温度时,才需要将栈顶温度弹出,进行比较;
class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int length = temperatures.length;
        int[] res = new int[length];
        Stack<Integer> stack = new Stack<>();
        stack.add(length - 1);
        int index = length - 2;

        while(index >= 0){
            while(!stack.isEmpty() && temperatures[index] >= temperatures[stack.peek()]){
                stack.pop();
            }

            if(stack.isEmpty()){
                res[index] = 0;
            }else{
                res[index] = stack.peek() - index;
            }

            stack.push(index);
            index--;
        }

        return res;
    }
}

2.下一个更大元素 I

力扣:496. 下一个更大元素 I

  • 首先找到nums2数组中,每个位置的元素离它最近的最大元素;
  • 将这个值给nums1;
class Solution {
    // nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        // 先去找到nums2中,i位置处,对应右侧中,比i位置元素大的第一个元素
        Stack<Integer> stack = new Stack<>();
        // 因为 nums2 没有重复元素,利用一个hashMap存储
        Map<Integer,Integer> map = new HashMap<>();
        // initialization
        int index = nums2.length;
        int[] ans = new int[nums1.length];

        map.put(nums2[index - 1], -1);
        stack.add(index - 1);
        index -= 2;

        while(index >= 0){
            while(!stack.isEmpty() && nums2[index] >= nums2[stack.peek()]){
                stack.pop();
            }

            if(stack.isEmpty()){
                map.put(nums2[index], -1);
            }else{
                map.put(nums2[index], nums2[stack.peek()]);
            }

            stack.push(index);
            index--;
        }

        for(int i =0; i < nums1.length; i++){
            ans[i] = map.get(nums1[i]);
        }

        return ans;
    }
}

3.下一个更大元素II

503.下一个更大元素II

class Solution {
    // 返回循环数组中每个元素的下一个更大元素
    public int[] nextGreaterElements(int[] nums) {
        int length = nums.length;
        int[] res = new int[length];
        // initialization
        Arrays.fill(res, -1);
        // 单调栈
        Stack<Integer> stack = new Stack<>();
        // two times
        for(int i = 0; i < 2 * length; i++){
            while(!stack.isEmpty() && nums[i % length] > nums[stack.peek()]){
                res[stack.peek()] = nums[i % length];
                stack.pop();
            }
            stack.push(i % length);
        }

        return res;
    }
}

4. 接雨水

42. 接雨水

思路:

  • 第一种方法是利用双指针,按照每一列深度进行计算;
  • 如果按照单调栈,就是按照每一行进行计算了。
class Solution {
    // 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
    /**
        height: 数组,柱子高度
     */
    public int trap(int[] height) {
        // base case
        int length = height.length;
        if(length <= 2){
            return 0;
        }
        // 定义一些变量
        int sum = 0;// 最后接的雨水值
        // 单调栈,栈顶到栈底是,按照元素值从小到大,但是存储的是数组中元素下标
        Stack<Integer> stack = new Stack<>();
        

        stack.push(0);
        for(int i = 1; i < length; i++){
            // 栈中是一定存在元素的
            int stackTopIndex = stack.peek();// 栈顶中元素对应在height数组中的下标

            // 如果新加入的元素,比栈顶的元素小,直接压入栈中
            if(height[i] < height[stackTopIndex]){
                stack.push(i);
            }else if(height[i] == height[stackTopIndex]){
                // 如果新加入的元素,和栈顶的元素值相同,弹出栈顶元素,并且将新元素加入栈中
                stack.pop();
                stack.push(i); 
            }else{
                // 情况三:要压入栈中的元素值,大于栈顶的元素值
                int top = stack.peek();
                // 遍历每一个比即将压入栈中小的元素
                while(!stack.isEmpty() && height[i] > height[top]){
                    int cur = stack.pop();// 当前元素为高度
                    // 算雨水量是根据宽度*高度
                    if(!stack.isEmpty()){
                        int left = stack.peek();
                        int h = Math.min(height[left], height[i]) - height[cur];
                        int w = i - left - 1;
                        int res = h * w;
                        sum += res;
                        top = stack.peek();
                    }         
                }

                stack.push(i);
            }
        }

        return sum;
    }
}

单调栈:

  • 需要按照每一行计算,保证元素有序;
  • 分为三种情况,即将要加入到栈中的元素和栈顶的元素大小关系,小于、等于、大于;
  • 只有当栈顶的元素小于入栈元素时,才需要去考虑更新结果;

在这里插入图片描述

class Solution {
    public int trap(int[] height) {
        int length =height.length;
        // base case
        if(length <= 2){
            return 0;
        }
        // 采用单调栈
        Stack<Integer> stack = new Stack<>();
        stack.push(0);
        int sum = 0;
        // traverse
        for(int i = 1; i < length; i++){
            int top = stack.peek();
            // condition one
            if(height[i] < height[top]){
                stack.push(i);
            }else if(height[i] == height[top]){
                // condition two
                stack.pop();
                stack.push(i);
            }else{
                // condition three
                while(!stack.isEmpty() && height[i] > height[top]){
                    int cur = stack.pop();
                    if(!stack.isEmpty()){
                        int left = stack.peek();
                        int h = Math.min(height[i], height[left]) - height[cur];
                        int w = i - left - 1;
                        int res = h * w;
                        sum += res; 
                        top = stack.peek();
                    }
                }
                stack.push(i);
            }
        }

        return sum;
    }
}

5.柱状图中最大的矩形

84.柱状图中最大的矩形

  • 这个是暴力求解:
class Solution {
    public int largestRectangleArea(int[] heights) {
        int length = heights.length;
        // 存储 i 位置,左边比它小的第一个元素的位置;
        int[] left = new int[length];
        left[0] = -1;
        for(int i = 1; i < length; i++){
            int index = i - 1;
            while(index >= 0 && heights[i] <= heights[index]){
                index--;
            }
            left[i] = index;
        }
        // 存储 i 位置,右边比它小的第一个元素的位置;
        int[] right = new int[length];
        right[length - 1] = length;
        for(int i = length - 2; i >= 0; i--){
            int index = i + 1;
            while(index <length && heights[i] <= heights[index]){
                index++;
            }
            right[i] = index;
        }
        // 遍历柱状图中每个柱子,更新最大矩形面积
        int ans = 0;
        for(int i = 0; i < length; i++){
            int h = heights[i];
            int w = right[i] - left[i] - 1;
            int res = h * w;
            // 判断
            ans = ans > res ? ans : res;
        }

        return ans;
    }
}
  • 方法二:进行单调栈
class Solution {
    // 单调栈
    /**
        需要明白,柱状图中的面积取决于三个数:
            左边比它小的柱状图,它自己,右边比它小的柱状图
        所以在单调栈中存储的元素,从栈顶到栈底的元素是从大到小的
     */
    public int largestRectangleArea(int[] heights) {
       int length = heights.length;
       int[] arr =  new int[length + 2];
       arr[0] = 0;
       arr[length + 1] = 0; 
       for(int i = 0; i < length; i++){
           arr[i + 1] = heights[i];
       }
       // 存储的是元素位置下标
       Stack<Integer> stack = new Stack<>();
       stack.push(0);
       // 结果
       int result = 0;
       for(int i = 1; i < length + 2; i++){
           int top = stack.peek();
           if(arr[i] > arr[top]){
               // 如果即将要压入栈中的元素,大于栈顶元素,就可直接压入栈中
               stack.push(i);
           }else if(arr[i] == arr[top]){
               // 如果即将要压入栈中的元素,等于栈顶元素,先将栈顶元素弹出,再压入栈顶中
               stack.pop();
               stack.push(i);
           }else{
               // 如果即将要压入栈中的元素,小于栈顶元素,这就是需要计算最大矩形面积的地方
               // 最大的矩形面积取决于三个数,当前柱状图的高度,左边界,有边界
               while(!stack.isEmpty() && arr[i] < arr[top]){
                   top = stack.pop();
                   if(!stack.isEmpty()){
                       int w = i - stack.peek() - 1;
                       int h = arr[top];
                        int ans = w * h;
                        result = result > ans ? result : ans;
                   }
                   top = stack.peek();
               }

               stack.push(i);
           }
       }

       return result;
    }
}

总结

重点:

  • 单调栈需要想好元素的添加顺序;
  • 需要明确栈中的栈顶到栈底,存放元素的大小顺序,是按照从小到大还是从大到小的顺序存放的;
  • 每次即将入栈的元素和栈顶元素的关系大小,导致选择变化
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值