首页 > 其他分享 >单调栈0

单调栈0

时间:2023-10-20 20:33:45浏览次数:32  
标签:柱子 int 元素 ans new stack 单调

通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了。时间复杂度为O(n)。

单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素高的元素,优点是整个数组只需要遍历一次。

栈中存放什么?下标!!

1. 每日温度

题目描述

题目链接
给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。

解题思路

  • 要求左边第一个比自己大的元素的位置,使用一个递减的单调栈。
  • 当碰到小于等于栈顶的元素时就入栈。
  • 当碰到大于栈顶的元素时,循环出栈并记录答案,直到栈顶元素大于等于当前元素,将当前元素入栈。

代码

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        Deque<Integer> stack = new LinkedList<>();  //存放下标
        int[] ans = new int[temperatures.length];
        stack.push(0);
        for(int i=1; i<temperatures.length; i++) {
            while(!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) {
                ans[stack.peek()] = i - stack.peek();
                stack.pop();
            }
            stack.push(i);
        }
        return ans;
    }
}

2. 下一个更大元素I

题目描述

题目链接
nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。

给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。

对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。

返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素 。

解题思路

  • 找到nums1在nums2中对应的元素的下一个更大元素,其实还是找nums2中元素的下一个更大元素。

  • 用单调栈找nums2的下一个更大元素,并对应到nums1中元素的下标即可。

  • 使用HashMap存nums1[i]:i

代码

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int[] ans = new int[nums1.length];
        Arrays.fill(ans, -1);
        //存放nums1[i]:i
        HashMap<Integer, Integer> map = new HashMap<>();
        for(int i=0; i<nums1.length; i++) {
            map.put(nums1[i], i);
        }
        Deque<Integer> stack = new LinkedList<>();
        stack.push(0);
        for(int i=1; i<nums2.length; i++) {
            while(!stack.isEmpty() && nums2[stack.peek()] < nums2[i]) {
                int key = nums2[stack.peek()];
                if(map.containsKey(key)) {
                    ans[map.get(key)] = nums2[i];
                }
                stack.pop();
            }
            stack.push(i);
        }
        return ans;
    }
}

3. 下一个更大元素II

题目描述

题目链接

给定一个循环数组 nums ( nums[nums.length - 1] 的下一个元素是 nums[0] ),返回 nums 中每个元素的 下一个更大元素 。

数字 x 的 下一个更大的元素 是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1 。

解题思路

  • 循环数组的下一个更大元素,其实把数组再遍历一遍就行了,就相当于拼接两个数组。

代码

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

4. 接雨水

题目描述

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

解题思路

方法1:双指针优化

  • 分别考虑每一列能装多少雨水

  • 每一列能装的雨水的高度取决于该列 左边最高的柱子和右边最高的柱子 中最矮的那个

  • 如何求左边最高的柱子的高度和右边最高的柱子的高度?动态规划,lHeight[i] = max(lHeight[i-1], height[i-1]),右边类似。

  • 注意:最两边的柱子不能装水

代码

class Solution {
    public int trap(int[] height) {
        int n = height.length;
        int[] lHeight = new int[n];
        int[] rHeight = new int[n];
        lHeight[0] = 0;
        for(int i=1; i<n; i++) {
            lHeight[i] = Math.max(lHeight[i-1], height[i-1]);
        }
        rHeight[n-1] = 0;
        for(int i=n-2; i>=0; i--) {
            rHeight[i] = Math.max(rHeight[i+1], height[i+1]);
        }
        int ans = 0;
        for(int i=1; i<n-1; i++) {
            int deta = Math.min(lHeight[i], rHeight[i]) - height[i];
            if(deta > 0) {
                ans += deta;
            }
        }
        return ans;
    }
}

方法2:单调栈(还是不太懂)

  • 对于当前柱子,只有在找到了下一个比它高的柱子时,这两个柱子中间才能装水。因此变成了单调栈能解决的问题。

  • 单调栈是递减的,因为一旦发现添加的柱子高度大于栈头元素了,此时就出现凹槽了,栈头元素就是凹槽底部的柱子,栈头第二个元素就是凹槽左边的柱子,而添加的元素就是凹槽右边的柱子。

  • 这时,是一层一层的计算的。

代码

class Solution {
    public int trap(int[] height) {
        Deque<Integer> stack = new LinkedList<>();
        stack.push(0);
        int ans = 0;
        for(int i=1; i<height.length; i++) {
            if(height[i] < height[stack.peek()]) {
                stack.push(i);
            } else if(height[i] == height[stack.peek()]) {
                stack.pop();
                stack.push(i);
            } else {    //凹槽的右边柱子
                while(!stack.isEmpty() && height[i] > height[stack.peek()]) {
                    int mid = stack.peek(); //凹槽
                    stack.pop();
                    if(!stack.isEmpty()) {  //凹槽的左边柱子
                        int h = Math.min(height[stack.peek()], height[i]) - height[mid];
                        int w = i - stack.peek() - 1;
                        ans += h * w;
                    }
                }
                stack.push(i);
            }
        }
        return ans;
    }
}

5. 柱状图中最大的矩形

题目描述

题目链接

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

解题思路(代码随想录的题解没看懂)

  • 找到每个柱子以自己的高度能扩展的最大面积中的最大值即可

  • 对于每个柱子, 以它的高度能扩展的最大面积,需要找左边第一个比它小的和右边第一个比它小的->单调栈

代码

class Solution {
    //对于每个柱子, 以它的高度能扩展的最大面积
    //需要找左边第一个比它小的和右边第一个比它小的
    public int largestRectangleArea(int[] heights) {
        int n = heights.length;
        int[] lHeight = new int[n];
        int[] rHeight = new int[n];
        Arrays.fill(lHeight, -1);
        Arrays.fill(rHeight, -1);
        //找左边第一个比它小的
        Deque<Integer> stack = new LinkedList<>();
        stack.push(n-1);
        for(int i=n-2; i>=0; i--) {
            while(!stack.isEmpty() && heights[i] < heights[stack.peek()]) {
                lHeight[stack.peek()] = i;
                stack.pop();
            }
            stack.push(i);
        }
        //找右边第一个比它小的
        stack = new LinkedList<>();
        stack.push(0);
        for(int i=1; i<n; i++) {
            while(!stack.isEmpty() && heights[i] < heights[stack.peek()]) {
                rHeight[stack.peek()] = i;
                stack.pop();
            }
            stack.push(i);
        }
        //计算以每个柱子的高度为矩形高度的扩展面积
        int ans = 0;
        for(int i=0; i<n; i++) {
            //area = (右边第一个比它矮的柱子的坐标 - 左边第一个比它矮的柱子的坐标 - 1) * 柱子高度
            //当左边没有比它矮的柱子时,从-1开始计算
            //当右边没有比它矮的柱子时,从n开始计算
            int area = ((rHeight[i] == -1 ? n : rHeight[i]) - (lHeight[i] == -1 ? -1 : lHeight[i]) - 1) * heights[i];
            ans = Math.max(ans, area);
        }
        return ans;
    }
}

标签:柱子,int,元素,ans,new,stack,单调
From: https://www.cnblogs.com/shimmer-ghq/p/17777081.html

相关文章

  • [最优化DP]决策单调性
    决策单调性的概念&证明工具:决策单调性,是在最优化dp中的可能出现的一种性质,利用它我们可以降低转移的复杂度。首先dp中会有转移,每个状态都由若干个状态转移而来,最优化dp比较特殊,只能由一个最优的状态转移而来。我们称之为某个状态的最优转移点。然后,dp会有一个转移顺序,......
  • 动态规划——决策单调性优化DP 学习笔记
    动态规划——决策单调性优化DP学习笔记决策单调性对于最优性问题,常有状态转移方程:\(f_i=\min/\max\{f_j\dots\}\),形象的:如果\(i\)的最优转移点是\(j\),\(i'\)的最优转移点是\(j'\),当\(i<i'\)时,有\(j\lej'\),则称该DP问题具有决策单调性。即:\(i\)单增,其最优转移点......
  • 告别单调的列表页,探索JVS低代码列表页设计的新思路
    列表页是什么?列表页是管理平台中的基础页面,核心的逻辑是实现数据的增删改查(CRUD),列表页核心的几个要素:页面内容的数据展示、查询条件、页面按钮及按钮触发的逻辑。列表页配置具备应用配置权限的用户,可以在列表页目录上,鼠标悬空,系统会弹出列表页设计的菜单,如下图所示:点击“设计页面”......
  • Slime Escape (CF D) (贪心, 双指针最大有效权值单调增长)
     补充:每次操作可以往左或者右走一步 思路:性质:以一边为重点使劲走,然后利用另外一边来给自己权值变大当这边要死了,就把这边回退到最大值,在走另一边,看另一边能到哪,这样每次都可以扩展最大值,于是利用双指针?也不是双指针,就是l,r分别贪心地向左和......
  • 【单调栈】总结
    单调栈有什么用?栈为容器,特性是后入先出。经典栈的应用场景大概为:浏览器的后退按钮实现等。即:栈的一个应用场景就是状态保持。单调栈和经典栈的区别是,栈是一股脑的存,单调栈是让栈内的元素(或者是栈内元素的对应元素)具有单调的特性。那这个单调的特性有啥用呢?我们不考虑其他的,只......
  • SDU Open 2023-H、几何、积分、单调栈维护上凸壳
    SDUOpen2023-H、几何、积分、单调栈维护上凸壳题目:https://codeforces.com/gym/104324/problem/H题意:有\(n\)个信号基站,你在边玩手机边走路,手机会自动连接到最近的基站。单位时间花费的流量是到基站距离的平方,现在从起点沿着直线走到终点,并且走的都是横平竖直的直线,单位时间......
  • 切比雪夫单调不等式(Chebyshev's monotonic inequality)(一般分配律)
    前置知识:一般分配律:\(\displaystyle\sum_{\substack{j\inJ\\k\inK}}a_jb_k\)\(=\displaystyle\sum_{\substack{j\inJ}}\displaystyle\sum_{\substack{k\inK}}a_jb_k\)\(=(\displaystyle\sum_{\substack{j\inJ}}a_j)(\displaystyle\sum_{\substac......
  • 多重背包单调队列优化
    引用自:动态规划-背包问题(01背包、完全背包、多重背包)#include<cstdio>#include<algorithm>#include<cstring>usingnamespacestd;constintmaxn=100005;intn,m,cnt;intv[102],num[102],dp[maxn];structQueue{intpos,val;}q[maxn];intmain(){......
  • [代码随想录]Day52-单调栈part03
    题目:84.柱状图中最大的矩形思路:实现要确定一个核心问题:包含完整一个柱子的最大矩形要找到这根柱子左侧最后一个高于他的柱子以及右侧最后一个高于他的柱子的位置(等同于左侧第一个小于他,右侧第一个小于他,因为+1-1就是)只要get到一个点,比如:30507080607040这一段柱子,在......
  • [代码随想录]Day51-单调栈part02
    题目:503.下一个更大元素II思路:总之就是走两次nums,可以拼接,也可以用下面的取余方式。代码:funcnextGreaterElements(nums[]int)[]int{lens:=len(nums)res:=make([]int,lens)fori:=0;i<lens;i++{res[i]=-1}stack:=make(......