折磨的死去活来的动态规划终于结束啦,今天秋秋给大家带来两题非常经典的单调栈问题,可能你不清楚单调栈是什么,可以用来解决什么问题,今天我们就来一步一步的逐渐了解单调栈,到能够灵活使用单调栈.注意以下讲解中,顺序的描述为 从栈头到栈底的顺序
通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了。时间复杂度为O(n)。这种情况我们使用暴力的两层for循环很明显是O(n^2)的时间复杂度.
原理就是用空间来换时间,用一个辅助栈来完成了一次循环做的事情,也就是记录下了前面已经遍历过的元素.更直白来说,就是用一个栈来记录我们遍历过的元素,因为我们遍历数组的时候,我们不知道之前都遍历了哪些元素,以至于遍历一个元素找不到是不是之前遍历过一个更小的,所以我们需要用一个容器(这里用单调栈)来记录我们遍历过的元素。
1.明确是单调递增栈还是单调递减栈
如果求一个元素右边第一个比他大的元素,就使用递增栈,如果是求比他小的元素就使用递减栈
当然左边也一样
2.明确单调栈里面放的是啥,表示什么意思
下面我们举个例子来直观感受一下单调栈的执行过程
首先这题肯定是一个单调增栈
接下来我们用temperatures = [73, 74, 75, 71, 71, 72, 76, 73]为例来逐步分析,输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。
1.首先先将第一个遍历元素加入单调栈(放入的是下标,因为很多情况下需要使用下标更好操作一点)
2.判断目前遍历的元素和栈顶元素的大小进行比较,有三种情况,大于等于小于
此时小于和等于的情况是一样的,我们直接将其入栈即可,要是大于我们就让栈顶元素出栈,一直比较,让所有比我目前遍历的元素小的元素都出栈,此时出栈的时候就是收割结果的时候,直接让当前元素和栈顶元素作差存入结果数组即可,如此循环往复即可得到答案.
有人说栈里面最后还会存有元素怎么办??
无需处理
最后无需处理的原因是因为初始化的时候已经确定了这种没有结果的默认值
利用上述单调栈的思路
我们在单调栈中存下标,进行比较的时候如果小于等于nums[栈顶元素]直接让其入栈,大于的话就将栈顶元素作为结果数组的下标,返回两者差值,切记:要将当前元素继续比较,直到不能比较为止,所以这里用的是while而不是if(也要记得不能比之后将目前元素入栈)
最后返回结果数组即可
- class Solution {
- public int[] dailyTemperatures(int[] temperatures) {
- int[] res = new int[temperatures.length];
- Stack
st = new Stack<>(); - st.push(0);
- for(int i = 1;i
- if(!st.isEmpty()&&temperatures[i]<=temperatures[st.peek()]){
- st.push(i);
- }else{
- while(!st.isEmpty()&&temperatures[i]>temperatures[st.peek()]){
- res[st.peek()] = i - st.peek();
- st.pop();
- }
- st.push(i);
-
- }
- }
- return res;
-
- }
- }
LeetCode T496 下一个最大元素I
题目链接:496. 下一个更大元素 I - 力扣(LeetCode)
题目思路:
相比于上面的题目,本题的意思是nums1对应在nums2中的相同数值的元素,求nums2中比此元素大的第一个元素的值,并返回长度和nums1相同,各个元素的求值结果.找不到返回-1
这里我们用示例2来理解一下题意,避免曲解题意
nums1 = [2,4]
nums2 = [1,2,3,4]
这里nums1的元素2对应在nums2中的2,其中第一个比他大的元素是3,所以res[0] = 3同理4在nums2中找不到比他还大的元素了,返回的就是res[1] = -1
这题的关键难点就是将这两个数组联系起来,我们可以使用map的方式将两个元素联系起来
这里由于元素都是唯一存在的,所以我们可以用map遍历第一个数组,key对应元素,value对应下标,这样我们在nums2中找到元素之后就可以利用这个map来得到其对应元素在nums1中的下标了,从而填入我们的结果数组中返回.
题目代码:
- class Solution {
- public int[] nextGreaterElement(int[] nums1, int[] nums2) {
- Stack
st = new Stack<>(); - Map
map = new HashMap<>(); - int[] res= new int[nums1.length];
- Arrays.fill(res,-1);
- for(int i = 0;i
- map.put(nums1[i],i);
- }
- st.push(0);
- for(int i = 1;i
- if(!st.isEmpty() && nums2[i]<=nums2[st.peek()]){
- st.push(i);
- }
- else{
- while(!st.isEmpty() && nums2[i]>nums2[st.peek()]){
- if(map.containsKey(nums2[st.peek()])){
- Integer index = map.get(nums2[st.peek()]);
- res[index] = nums2[i];
- }
- st.pop();
- }
- st.push(i);
- }
- }
- return res;
-
-
-
- }
- }
-
相关阅读:
Sharding-JDBC源码解析与vivo的定制开发
YARP:探索反向代理的新境界
操作系统-《王道 操作系统》
本地录像视频文件如何推送到视频监控平台EasyCVR进行AI视频智能分析?
【接口】HTTP(3) |GET和POST两种基本请求方法有什么区别
谈谈分布式事务原理
前端开发规范
【斗破年番】火火小医仙幽会,彩鳞吃醋跟随,失身之事终暴露,蛇人族来算账
Prometheus + Grafana 搭建监控仪表盘
如何将「知识」体系化管理
-
原文地址:https://blog.csdn.net/qiuqiushuibx/article/details/134480108