单调栈 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;}
};

单调栈暂时就刷到这儿,接下来继续刷双指针的题目吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/377553.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python 抠图 锯齿_Python | 绘图中的抗锯齿

python 抠图 锯齿Antialiasing is another important feature of Matplotlib and in this article, we will review how to use this functionality. pyplot.antialiased() is an inbuilt function in matplotlib.pyplot which performs our required operation. 抗锯齿是Matpl…

apk 反编译

引用&#xff1a;http://code.google.com/p/dex2jar/issues/detail?id20 最新版:dex2jar http://code.google.com/p/dex2jar/downloads/list 错误&#xff1a;http://code.google.com/p/dex2jar/issues/detail?id20 这段时间在学Android应用开发&#xff0c;在想既然是用Jav…

OpenDiscussion_DataDrivenDesign

本文源于公司内部技术交流&#xff0c;如有不当之处&#xff0c;还请指正。 Content&#xff1a; 1. What is Data-driven design?2. WPF revolution.3. More about ObservableCollection.4. Question.1. What is Data-driven design?Data-driven design: is a design of usi…

十三、Loss Functions

一、Loss Functions损失函数 损失函数的作用&#xff1a; 1&#xff0c;损失函数就是实际输出值和目标值之间的差 2&#xff0c;由这个差便可以通过反向传播对之后的数据进行更新 Loss Functions官网给的API 里面由很多种损失函数&#xff0c;不同的损失函数有其不同的用途及表…

linux命令行界面_Linux中的命令行界面

linux命令行界面If you are like most people, you are probably most familiar with using a Graphical User Interface (GUI) to control your computer. Introduced to the masses by Apple on the Macintosh computer and popularized by Microsoft, a GUI provides an eas…

十四、OPTIM

一、torch.optim torch.optim.Optimizer(params, defaults)优化器官网说明 由官网给的使用说明打开看出来优化器实验步骤&#xff1a; ①构造选择优化器 例如采用随机梯度下降优化器SGD torch.optim.SGD(beyond.parameters(),lr0.01)&#xff0c;放入beyond模型的参数param…

leetcode 滑动窗口小结 (二)

目录424. 替换后的最长重复字符思考分析1优化1004. 最大连续1的个数 III友情提醒方法1&#xff0c;基于当前最大频数方法2&#xff0c;基于历史最大频数424. 替换后的最长重复字符 https://leetcode-cn.com/problems/longest-repeating-character-replacement/ 给你一个仅由大…

十五、修改VGG16网络来适应自己的需求

一、VGG-16 VGG-16神经网络是所训练的数据集为ImageNet ImageNet数据集中验证集和测试集一万五千张&#xff0c;有一千个类别 二、加载VGG-16神经网络模型 VGG16模型使用说明 torchvision.models.vgg16(pretrainedFalse) 其中参数pretrained表示是否下载已经通过ImageNet数…

十六、保存和加载自己所搭建的网络模型

一、保存自己搭建的模型方法一 例如&#xff1a;基于VGG16网络模型架构的基础上加上了一层线性层&#xff0c;最后的输出为10类 torch.save(objmodule,f"path")&#xff0c;传入需要保存的模型名称以及要保存的路径位置 保存模型结构和模型的参数&#xff0c;保存文…

uC/OS-II OS_TASK.C中有关任务管理的函数

函数大致用途 OS_TASK.C是uC/OS-II有关任务管理的文件&#xff0c;它定义了一些函数&#xff1a;建立任务、删除任务、改变任务的优先级、挂起和恢复任务&#xff0c;以及获取有关任务的信息。 函数用途OSTaskCreate()建立任务OSTaskCreateExt()扩展建立任务OSTaskStkChk()堆…

Scala中的do ... while循环

做...在Scala循环 (do...while loop in Scala) do...while loop in Scala is used to run a block of code multiple numbers of time. The number of executions is defined by an exit condition. If this condition is TRUE the code will run otherwise it runs the first …

十七、完整神经网络模型训练步骤

以CIFAR-10数据集为例&#xff0c;训练自己搭建的神经网络模型架构 一、准备CIFAR-10数据集 CIFAR10官网使用文档 torchvision.datasets.CIFAR10(root"./CIFAR_10",trainTrue,downloadTrue) 参数描述root字符串&#xff0c;指明要下载到的位置&#xff0c;或已有数…

μC/OS-Ⅱ 操作系统内核知识

目录μC/OS-Ⅱ任务调度1.任务控制块2.任务管理3.任务状态μC/OS-Ⅱ时间管理μC/OS-Ⅱ内存管理内存控制块MCBμC/OS-Ⅱ任务通信1.事件2.事件控制块ECB3.信号量4.邮箱5.消息队列操作系统内核&#xff1a;在多任务系统中&#xff0c;提供任务调度与切换、中断服务 操作系统内核为每…

第二版tapout

先说说上次流回来的芯片的测试情况。 4月23日&#xff0c; 芯片采用裸片直接切片&#xff0c; bond在板子上&#xff0c;外面加了一个小塑料壳来保护&#xff0c;我们就直接拿回来测试了。 测试的主要分为模拟和数字两部分&#xff0c; 数字部分的模块基本都工作正常&#xff0…

十八、完整神经网络模型验证步骤

网络训练好了&#xff0c;需要提供输入进行验证网络模型训练的效果 一、加载测试数据 创建python测试文件&#xff0c;beyond_test.py 保存在dataset文件夹下a文件夹里的1.jpg小狗图片 二、读取测试图片&#xff0c;重新设置模型所规定的大小(32,32)&#xff0c;并转为tens…

二分法变种小结(leetcode 34、leetcode33、leetcode 81、leetcode 153、leetcode 74)

目录二分法细节1、leetcode 34 在排序数组中查找元素的第一个和最后一个位置2、不完全有序下的二分查找(leetcode33. 搜索旋转排序数组)3、含重复元素的不完全有序下的二分查找(81. 搜索旋转排序数组 II)3、不完全有序下的找最小元素(153. 寻找旋转排序数组中的最小值)4、二维矩…

ID3D11DeviceContext::Dispatch与numthread笔记

假定——[numthreads(TX, TY, TZ)] // 线程组尺寸。既线程组内有多少个线程。Dispatch(GX, GY, GZ); // 线程组的数量。既有多少个线程组。 那么——SV_GroupThreadID{iTX, iTY, iTZ} // 【线程组内的】线程3D编号SV_GroupID{iGX, iGY, iGZ} // 线程组的3D编号SV_DispatchT…

小米手环6解决天气未同步问题

最近我发现了我的米6手环天气不同步&#xff0c;打开Zepp Life刷新同步也不行&#xff0c;后来我找了一些网上的解决方法&#xff0c;尝试了一些也还不行&#xff0c;我这人喜欢瞎捣鼓&#xff0c;无意之间给整好了&#xff0c;后来我开始总结自己操作步骤&#xff0c;就在刚才…

C++ 内存分配层次以及memory primitives的基本用法

分配层次 C memory primitives 分配释放类型是否可重载mallocfree()C函数不可newdeleteC表达式不可::operator new()::operator delete()C函数可allocator::allocate()allocator::deallocate()C标准库可自由设计并以之搭配任何容器 分配与释放的四个用法 1、malloc and delet…

一、Pytorch对自定义表达式自动求导

例如&#xff1a;y ax bx c&#xff0c;分别对a&#xff0c;b&#xff0c;c求导 若当a3&#xff0c;b4&#xff0c;c5&#xff0c;x1时 import torch from torch import autogradx torch.tensor(1.) a torch.tensor(3.,requires_gradTrue) b torch.tensor(4.,requires…