单调栈 leetcode整理(三)
目录
- 42. 接雨水
- 思路分析
- 901. 股票价格跨度
- 思路
- 581. 最短无序连续子数组
- 思路一:排序+双指针
- 思路二:单调栈
- 思路三:双指针(最省时)
42. 接雨水
42. 接雨水
给定 n 个非负整数表示每个宽度为 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 个单位的雨水(蓝色部分表示雨水)。
示例 2:
输入:height = [4,2,0,3,2,5] 输出:9
提示:
n == height.length
0 <= n <= 3 * 10^4
0 <= height[i] <= 10^5
思路分析
由于这一题方法比较多,就专门写了一个:
https://blog.csdn.net/qq_42604176/article/details/111053090
901. 股票价格跨度
编写一个 StockSpanner 类,它收集某些股票的每日报价,并返回该股票当日价格的跨度。
今天股票价格的跨度被定义为股票价格小于或等于今天价格的最大连续日数(从今天开始往回数,包括今天)。
例如,如果未来7天股票的价格是 [100, 80, 60, 70, 60, 75, 85],那么股票跨度将是 [1, 1, 1, 2, 1, 4, 6]。
示例:
输入:[“StockSpanner”,“next”,“next”,“next”,“next”,“next”,“next”,“next”], [[],[100],[80],[60],[70],[60],[75],[85]]
输出:[null,1,1,1,2,1,4,6]
解释:
首先,初始化 S = StockSpanner(),然后:
S.next(100) 被调用并返回 1,
S.next(80) 被调用并返回 1,
S.next(60) 被调用并返回 1,
S.next(70) 被调用并返回 2,
S.next(60) 被调用并返回 1,
S.next(75) 被调用并返回 4,
S.next(85) 被调用并返回 6。
注意 (例如) S.next(75) 返回 4,因为截至今天的最后 4 个价格
(包括今天的价格 75) 小于或等于今天的价格。
思路
仍然是单调栈的思路,构造一个单调递减栈。
1、如果当前栈为空,输入元素在原数组中的下标压入栈中。
获取该元素左边极大值的坐标,此时栈为空,说明左边没有比该元素还小的元素,所以左边极大值坐标就是不存在,填-1(不能填0)
然后返回长度就是当前元素在原数组中下标 - 左极大值坐标。
可以想象一下,如果左边极大值不存在的时候,我们填入0,而当前输入元素正好是第0个元素,那么显然结果就是0 - 0 = 0 ,不符合答案1。
2、如果当前栈不为空,并且栈顶元素小于等于输入元素。
此时根据题目要求:小于或等于今天价格的最大连续日数,我们必须要找到大于今日价格才能停止寻找,所以仍然需要回撤操作。
直到栈为空或者栈顶元素大于今日价格,才停止回撤。
如果栈为空,说明左边没有比该元素还小的元素,所以左边极大值坐标就是不存在,填-1(不能填0)
如果栈不为空,左极大值坐标就是当前栈顶元素坐标。
最后将今日 - 左极大值日期,就是结果
和之前系列中有的操作还是相似的,例如栈中记录的不是元素,而是该元素在原数组中的下标。
对于栈顶元素与当前元素的比较,仍然是需要结合题意,列出三种可能:当前元素 < 、 == 、 > st.back(),找到符合题意的出栈标准
class StockSpanner {private:vector<int> input;vector<int> st;
public:StockSpanner() {}int next(int price) {//输入一个price,input数组又多了一个元素input.emplace_back(price);//该元素为input数组末尾int now_index = input.size() - 1;//如果此时栈为空,说明它左边没有值while(!st.empty() && input[st.back()] <= price){st.pop_back();}//获取该元素左边极大值的下标,如果栈为空的话,说明没有,那么left_max就是-1;如果不为空,返回栈顶int left_max_index;if(st.empty()) left_max_index = -1;else left_max_index = st.back();//当前元素入栈st.push_back(now_index);return (now_index - left_max_index);}
};/*** Your StockSpanner object will be instantiated and called as such:* StockSpanner* obj = new StockSpanner();* int param_1 = obj->next(price);*/
581. 最短无序连续子数组
给定一个整数数组,你需要寻找一个连续的子数组,如果对这个子数组进行升序排序,那么整个数组都会变为升序排序。
你找到的子数组应是最短的,请输出它的长度。
示例 1:
输入: [2, 6, 4, 8, 10, 9, 15]
输出: 5
解释: 你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。
说明 :
输入的数组长度范围在 [1, 10,000]。
输入的数组可能包含重复元素 ,所以升序的意思是<=。
思路一:排序+双指针
先排序,然后将排序后的数组与原数组进行比对(从左到右、从右到左)。
找到左右边界,然后最后的结果就是左右边界差值+1.
当然还要考虑特殊情况:原数组本身是单调递增或递减的,这样我们就不能对左右边界进行更新。
但是我们知道单调的结果:无非是0(单调递增不需要重新排序),和nums.size()(单调递减需要将整个数组都排序).
class Solution {public:int findUnsortedSubarray(vector<int>& nums) {vector<int> _new = nums;sort(_new.begin(),_new.end());int left_index = -1;int right_index = -1;//双指针left、right,从两边向中间遍历for(int left = 0; left < nums.size(); left++){if(nums[left] != _new[left]){left_index = left;break;}}for(int right = nums.size() - 1; right >= 0; right--){if(nums[right] != _new[right]){right_index = right;break;}}//如果是单调递增,不需要修改,返回0if(left_index == -1) return 0;//如果是单调递减,返回整个长度if(right_index == -1) return nums.size();return (right_index - left_index + 1);}
};
思路二:单调栈
背后的思想仍然是选择排序,我们需要找到无序子数组中最小元素和最大元素分别对应的正确位置。
来求我们需要的无序子数组的边界。
使用栈,从头遍历nums数组:
1、如果遇到的数组大小一直升序的,我们就不断把对应的下标压入栈中,目的:这些元素目前都是出于正确的位置上
2、一旦当前数字比栈顶元素小,那么我们知道nums[j]一定不在正确的位子上
3、既然这样,我们就需要找到nums[j]的正确位置:
不断将栈顶元素弹出,知道栈顶元素比nums[j],假设此刻栈顶元素对应的下标是k,那么我们知道nums[j]的正确下标应该是k+1
4、重复上述过程,直到遍历完整个数组,这样我们可以找到最小的k,它也是无序子数组的左边界。
5、类似的,我们逆序遍历一遍nums数组来找到无序子数组的右边界。这一次我们将降序的元素压入栈中。
6、如果遇到一个升序的元素,不断将栈顶元素弹出,直到找到一个更大的元素,以此找到无序子数组的右边界
class Solution {public:int findUnsortedSubarray(vector<int>& nums) {vector<int> st;int left = nums.size() - 1;int right = 0;for(int i = 0; i < nums.size(); i++){while(!st.empty() && nums[i] < nums[st.back()]){left = min(left,st.back()); st.pop_back();}st.emplace_back(i);}st.clear();for(int i = nums.size() - 1; i >= 0; i--){while(!st.empty() && nums[i] > nums[st.back()]){right = max(right,st.back()); st.pop_back();}st.emplace_back(i);}return right - left > 0 ? right - left + 1 : 0;}
};
思路三:双指针(最省时)
这一题的双指针解法和接雨水的双指针思想有一定相似性:
leetcode 42. 接雨水 思考分析(暴力、动态规划、双指针、单调栈)
我们要做的是,找到无序数组的上下界。
运用到本题,就是下面两个想法:
https://leetcode-cn.com/problems/shortest-unsorted-continuous-subarray/solution/shi-jian-chao-guo-100de-javajie-fa-by-zackqf/
https://leetcode-cn.com/problems/shortest-unsorted-continuous-subarray/solution/jian-dan-zhi-guan-de-shuang-zhi-zhen-fa-by-sillywo/
无序子数组中最小元素的正确位置可以决定左边界,最大元素的正确位置可以决定右边界。
寻找右边界:
从左往右遍历,用max记录遍历过的最大值,如果max大于当前nums[i],说明nums[i]的位子不正确,属于需要排序的数组,所以右边界就需要更新为i
如果nums[i]大于max更新max,继续往右检查,是否有元素比更新之后的max要小;最终可以找到需要排序的数组的右边界,右边界之后的元素都大于max。
寻找左边界:
从右向左遍历,yongmin记录当前遍历过的最小值,如果min小于当前nums[i],说明nums[i]的位子不正确,属于需要排序的数组,所以更新左边界
如果nums[i]小于min更新min,继续往左检查,是否有元素比更新之后的min要大,最终可以找到需要排序的数组的左边界,左边界之前的元素都小于min
class Solution {public:int findUnsortedSubarray(vector<int>& nums) {//特判int n = nums.size();if (n <= 1) {return 0;}//从右到左找下界,从左到右找上界int left = n - 2, right = 1; int curMin = nums[n - 1], curMax = nums[0];int up = 0, down = 1;//升序时移动 curMin 和 curMax//逆序时移动 down 和 up//不论顺序如何,双指针 left 和 rigt 一直保持移动while (left >=0 && right < n) {if (nums[left] > curMin){down = left;}else {curMin = nums[left];}left--;if (nums[right] < curMax) {up = right;}else {curMax = nums[right];}right++;}return up - down + 1;}
};
单调栈暂时就刷到这儿,接下来继续刷双指针的题目吧。
单调栈 leetcode整理(三)相关推荐
- 单调栈 leetcode整理(二)
目录 为什么单调栈的时间复杂度是O(n) 496. 下一个更大元素 I 方法一:暴力 方法二:单调栈+哈希表 739. 每日温度 单调栈模版解 优化 503. 下一个更大元素 II 单调栈+循环遍历 ...
- 单调栈 leetcode整理(一)
目录 单调栈知识 402. 移掉K位数字 1673. 找出最具竞争力的子序列 316. 去除重复字母(1081. 不同字符的最小子序列) 321. 拼接最大数 单调栈知识 单调栈就是一个内部元素有序的 ...
- 代码随想录1刷—单调栈篇
代码随想录1刷-单调栈篇 什么时候想到单调栈? 单调栈的原理? 单调栈工作过程? [739. 每日温度](https://leetcode.cn/problems/daily-temperatures ...
- 数据结构算法 | 单调栈
文章目录 算法概述 题目 下一个更大的元素 I 思路 代码 下一个更大元素 II 思路 代码 132 模式 思路 代码 接雨水 思路 算法概述 当题目出现 「找到最近一个比其大的元素」 的字眼时,自然 ...
- [力扣刷题总结](栈和单调栈篇)
文章目录 ~~~~~~~~~~~~栈~~~~~~~~~~~~ 155. 最小栈 解法1:链表 剑指 Offer 31. 栈的压入.弹出序列 解法1:模拟栈 20. 有效的括号 解法1:栈 相似题目: ...
- LeetCode 402. 移掉K位数字(贪心,单调栈)
1. 题目 给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小. 注意: num 的长度小于 10002 且 ≥ k. num 不会包含任何前导零. 示例 1 : ...
- LeetCode #121 买卖股票的最佳时机 贪心 单调栈 动态规划
LeetCode #121 买卖股票的最佳时机 题目描述 给定一个数组,它的第 iii 个元素是一支给定股票第 iii 天的价格. 如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算 ...
- leetcode84- 柱状图中最大的矩形(三种思路:暴力,单调栈+哨兵(详解),分治)
leetcode84- 柱状图中最大的矩形(三种思路:暴力,单调栈+哨兵(详解),分治) 介绍 题目 解题思路 解法一:暴力向两边搜索 解法二:单调栈 画图演示 宽度计算: 解法三:单调栈+哨兵 解法 ...
- LeetCode第 57 场力扣夜喵双周赛(差分数组、单调栈) and 第 251 场力扣周赛(状态压缩动规,树的序列化,树哈希,字典树)
LeetCode第 57 场力扣夜喵双周赛 离knight勋章越来越近,不过水平没有丝毫涨进 1941. 检查是否所有字符出现次数相同 题目描述 给你一个字符串 s ,如果 s 是一个 好 字符串,请 ...
最新文章
- Socket网络编程--简单Web服务器(2)
- PMCAFF问答精选 | 对于用户反馈,产品经理需要第一时间给出回应吗?
- 技术玩法大升级,网易MCtalk揭秘5G即时通讯技术背后的秘密
- liunx--账户文件权限和管理(账户添加删除,组的添加和删除 文件的归宿和权限)
- 使用ANT打包Android应用
- 强制消除Xcode警告的方法
- android html转pdf工具,android – 使用iText库将html转换为pdf时未应用hr的内联CSS
- WEB安全基础-文件操作漏洞
- Centos 6.9 编译安装gcc 4.8.5
- 实验二 (2)优先数调度
- Java配置文件读取写入通用类库:PropUtils 属性文件类
- ILSpy查看dll内容
- flash实验中需添加的flash.c文件
- java实现ABAC
- c语言 建立测井数据结构体,给定C语言的数据结构 struct T { int w; union T { char c; int i; dou...
- pdf解密,pdf,jpg,word格式互相转换
- mysql查询连续三天100以上_一个SQL查询连续三天的流量100以上的数据值【SQql Server】...
- 「Deep Learning」读书分享系列
- ITOF vs DTOF
- OpenGL学习之路(四)
热门文章
- 大学生助学贷款如何还利息(本金+利息都可以)
- qfp封装能够linux,QFP、PQFP、LQFP、TQFP封装形式及PCB详解
- 在 Snoop 中使用 PowerShell 脚本进行更高级的 UI 调试
- PL/SQL 08 异常 exception
- 辨异 —— 行星 vs 恒星
- node--更新数据库问题
- (接口)银联证书上传被修改的问题和读取证书的绝对路径问题
- 恋爱Linux(Fedora20)2——安装Java运行环境(JDK)
- [LeetCode] Maximal Rectangle
- Moon.Orm性能报告