代码随想录算法训练营第60期第五十一天打卡

article/2025/8/23 23:26:51

        大家好,昨天我们结束了动态规划的题目,其实我们可能还没有完全理解那些题目的真正含义,那其实很正常大家多复习几遍就可以了,那我们今天就将开始一个全新的章节,它就是单调栈,那关于什么是单调栈,我们如何使用单调栈解决题目,我们讲解之后大家就会一目了然,我们就开始今天的题目。

第一题对应力扣编号为739的题目每日温度

        其实我们在开始这道题目的时候我们需要先讲解一下单调栈的理论基础,不然大家压根不知道什么是单调栈,通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了。时间复杂度为O(n)。单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素高的元素,优点是整个数组只需要遍历一次。当然我们在使用单调栈的时候,我们需要注意以下几点:1.单调栈里存放的元素是什么?单调栈里只需要存放元素的下标i就可以了,如果需要使用对应的元素,直接T[i]就可以获取。第二点单调栈里元素是递增呢? 还是递减呢?这个其实顺序的描述为 从栈头到栈底的顺序,这里我们要使用递增循序(再强调一下是指从栈头到栈底的顺序),因为只有递增的时候,栈里要加入一个元素i的时候,才知道栈顶元素在数组中右面第一个比栈顶元素大的元素是i。其实我们就直接看一下这道题目的题目要求:

       题目是要求我们求下一个比自身大的元素的位置,其实很明显我们通过上面对单调栈的了解我们就可以推断这道题目我们就可以使用单调栈来解决,那我们就一起来看一下题目的解题思路:其实这道题目我要把每一个地方都给大家模拟一年比较麻烦,我就给大家解释一部分剩下的都是同理的,我们首先要搞清楚我们这里单调栈我们是使用单调递增的单调栈,我们使用单调栈存在如下几种情况:(1)当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况(2)当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况(3)当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况,那我们就看一下本题的大致的模拟单调栈的过程,首先先将第一个遍历元素73加入单调栈,随后加入T[1] = 74,因为T[1] > T[0](当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况)。如果我们要保持一个递增单调栈(从栈头到栈底),所以将T[0]弹出,T[1]加入,此时result数组可以记录了,result[0] = 1,即T[0]右面第一个比T[0]大的元素是T[1]。其实后面的都是这种思路,我们继续看几个,下一个元素是75,同理,T[1]弹出,这样我们就可以更新result数组,我们result数组起初都是初始化为0,这样其实我们找到最大值的时候由于右边不存在比最大值还大的元素就不会更新保持为0,接下来就是加入T[3] = 71,T[3] < T[2] (当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况),加T[3]加入单调栈。加入T[4],T[4] == T[3] (当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况),此时依然要加入栈,不用计算距离,因为我们要求的是右面第一个大于本元素的位置,而不是大于等于!到这里大家差不多就可以明白我们单调栈的模拟过程,我们是跟栈顶元素比较,这样的话我们就尝试给出本题的解题代码:

class Solution {
public:vector<int> dailyTemperatures(vector<int>& temperatures) {stack<int> st; //定义单调栈//定义我们的结果数组里面存储的就是下一个更高温出现在几天后vector<int> result(temperatures.size(), 0);//注意我们存入单调栈的元素应该是数组下标不是元素的具体值st.push(0);for (int i = 1; i < temperatures.size(); ++i){//如果当前的值小于栈顶元素其实它并不会影响我们寻找比当前栈顶元素的温度高的下一个高温出现在几天后if (temperatures[i] < temperatures[st.top()]){st.push(i);}else if (temperatures[i] == temperatures[st.top()]){st.push(i);}else{while(!st.empty() && temperatures[i] > temperatures[st.top()]){//此时我们其实就可以找到当前栈顶元素的温度高的下一个高温出现在几天后result[st.top()] = i - st.top();//找完了的要pop掉让下面的元素继续找st.pop();}st.push(i);}}return result;}
};

       其实大家要注意我们要去理解单调栈的模拟过程,我们其实只有发现比当前栈顶元素大的元素的时候我们才可以得到比当前栈顶元素温度高的下一个天,还有大家注意我们的单调栈里面存放的应该是元素的下标而不是元素值,这样我们方便求是第几天,大家注意这一些就可以。

第二题对应力扣编号为496的题目下一个更大元素 I

        我们来到今天的第二题,经过上一道题大家应该可以对单调栈有大致的了解,那其实听到这道题我们就可以看出本题就是使用单调栈来解决,因为题目要求我们求的是比当前元素大的下一个元素,我们就先来看一下题目的具体要求:

        题目意思稍微有点抽象,其实我们是返回一个nums1长度的数组,我们是需要先在nums2中找到与nums1各个元素相等的对应位置的下标,然后在这个下标之后去找第一个大于nums1中元素的元素值,注意我们是要找到具体的元素不是下标,这样我们很清楚这道题我们一定是使用单调栈来解决,首先注意题目说如果不存在对应位置就输出 -1 ,所以result数组如果某位置没有被赋值,那么就应该是是-1,所以就初始化为-1。在遍历nums2的过程中,我们要判断nums2[i]是否在nums1中出现过,因为最后是要根据nums1元素的下标来更新result数组。注意题目中说是两个没有重复元素 的数组 nums1 和 nums2。就是这两个数组中每两个元素都是不同的,没有重复元素,我们就可以用map来做映射了。根据数值快速找到下标,还可以判断nums2[i]是否在nums1中出现过。这个其实在哈希表章节里面提到过,

      本题单调栈栈头到栈底的顺序,要从小到大,也就是保持栈里的元素为递增顺序。只要保持递增,才能找到右边第一个比自己大的元素。这样的话我们就还是考虑几种情况:

  1. 情况一:当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
  2. 情况二:当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
  3. 情况三:当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况

       第一种情况:此时满足递增栈(栈头到栈底的顺序),所以直接入栈。第二种情况:如果相等的话,依然直接入栈,因为我们要求的是右边第一个比自己大的元素,而不是大于等于!第三种情况:此时如果入栈就不满足递增栈了,这也是找到右边第一个比自己大的元素的时候。判断栈顶元素是否在nums1里出现过,(注意栈里的元素是nums2的元素),如果出现过,开始记录结果。因为我们题目所求的元素都是来自nums2,此时栈顶元素在nums2数组中右面第一个大的元素是nums2[i],这里其实很绕容易迷糊。这样的话我们经过以上的分析就可以尝试给出代码:

class Solution {
public:vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {//定义单调栈stack<int> st;//定义存放结果的数组vector<int> result(nums1.size(), -1);//特殊情况最好考虑进来if (nums1.size() == 0) return result;unordered_map<int, int> umap;for (int i = 0; i < nums1.size(); ++i) umap[nums1[i]] = i;//模拟单调栈的过程st.push(0);for (int i = 1; i < nums2.size(); ++i){if (nums2[i] < nums2[st.top()]) st.push(i);else if (nums2[i] == nums2[st.top()]) st.push(i);else{while(!st.empty() && nums2[i] > nums2[st.top()]){//首先需要去看当前的元素是否在nums1中出现过if (umap.count(nums2[st.top()]) > 0){//如果出现过我们就可以收集结果了注意这个地方对应的是nums1的索引其实不难理解因为题目就是返回nums1长度的数组//而且我们的操作对象其实也是nums1里面的元素int index = umap[nums2[st.top()]];result[index] = nums2[i];}st.pop();}st.push(i);}}return result;}
};

        这道题目的难度比较大,不仅仅涉及到了我们目前的单调栈的相关理论知识还涉及到了我们以前的哈希表的相关理论知识,包括我们如何判断一个数组里的元素是否在其他数组出现过,这个需要大家去复习而且需要综合使用我们以前学过的所有方法,难度较大,大家慢慢理解。

第三题对应力扣编号503的题目下一个更大元素II

        这是我们今天的最后一道题目,其实与上一道题目一定是异曲同工的,只不过上一道题目其实一点也不简单,这道题目估计也不会简单,那我们就直接看一下这道题目的题目要求:

        这道题其实有点奇怪的地方就是我们可以循环搜索这个数组,大家看我们上面的示例,其实我们的数组表面上是[1,2,1]其实是[1,2,1,1,2,1,1,2,1....]这样我们自然可以找到比最后一个1大的元素就是2,那我们就看看这道题的解题思路:其实这道题大家时候考虑我将两个数组接起来然后使用我们的单调栈来解决问题,这个其实是可以的,如果存在这样的元素的话其实我们把数组重复一遍就可以,那我们可以先尝试一下这种解题思路:

class Solution {
public:vector<int> nextGreaterElements(vector<int>& nums) {//数组重复的过程vector<int> nums1(nums.begin(), nums.end());nums.insert(nums.end(), nums1.begin(), nums1.end());//初始化result数组vector<int> result(nums.size(), -1);if (nums.size() == 0) return result;//开始定义单调栈stack<int> st;st.push(0);for (int i = 1; i < nums.size(); ++i){if (nums[i] < nums[st.top()]) st.push(i);else if (nums[i] == nums[st.top()]) st.push(i);else{while(!st.empty() && nums[i] > nums[st.top()]){result[st.top()] = nums[i];st.pop();}st.push(i);}}result.resize(nums.size() / 2);return result;}
};

        大家如果明白了上一道题目相信这种思路并不难理解,其实大家一刷把这道题理解道这种程度已经很不错了,但这样其实很耗费空间,而且做了很多的无用操作,其实为了避免一些无效操作我们可以直接单调栈模拟的过程就跑两遍数组:

class Solution {
public:vector<int> nextGreaterElements(vector<int>& nums) {vector<int> result(nums.size(), -1);if (nums.size() == 0) return result;stack<int> st;st.push(0);for (int i = 1; i < nums.size() * 2; i++) { // 模拟遍历两边nums,注意一下都是用i % nums.size()来操作if (nums[i % nums.size()] < nums[st.top()]) st.push(i % nums.size());else if (nums[i % nums.size()] == nums[st.top()]) st.push(i % nums.size()); else {while (!st.empty() && nums[i % nums.size()] > nums[st.top()]) {result[st.top()] = nums[i % nums.size()];st.pop();}st.push(i % nums.size());}}return result;}
};

今日总结

        今天的单调栈其实是一种很巧妙的做法,希望大家理解这种做法,还有我们要清楚我们单调栈是如何使用的就可以了,今天我们的分享就到这里,我们明天再见!


http://www.hkcw.cn/article/KeLSbfNXEv.shtml

相关文章

苹果Siri升级搁浅:轻资产路线受阻 AI竞争暴露短板

据外媒Business Insider报道,谷歌上周高调发布AI视频工具Flow之际,苹果被迫推迟了生成式AI版Siri的核心升级计划。这一突发状况暴露了苹果在技术上的短板:缺乏自研AI芯片、数据中心依赖谷歌设施、训练数据受限于隐私政策。与谷歌25年来构建的12层技术栈相比,苹果自研AI芯片…

RV1126 + FFPEG多路码流项目

代码主体思路&#xff1a; 一.VI,VENC,RGA模块初始化 1.先创建一个自定义公共结构体&#xff0c;用于方便管理各个模块 rkmedia_config_public.h //文件名字#ifndef _RV1126_PUBLIC_H #define _RV1126_PUBLIC_H#include <assert.h> #include <fcntl.h> #include …

Mybatis中的懒加载

目录 基本概念 懒加载的应用场景 如何配置懒加载 全局配置 局部配置 懒加载的工作原理 示例代码 一对一懒加载 一对多懒加载 懒加载的触发条件 懒加载的优缺点 优点&#xff1a; 缺点&#xff1a; 解决N 1查询问题的方法 注意事项 示例 对应sql语句 当只需输…

无人机桥梁3D建模的拍摄频率

无人机桥梁3D建模的拍摄频率 无人机桥梁3D建模的拍摄频率&#xff08;每秒拍摄照片数&#xff09;需根据建模精度、飞行速度、相机性能等因素综合确定。以下是专业级作业的详细参数分析&#xff1a; 1. 核心计算公式 拍摄频率&#xff08;fps&#xff09; \frac{飞行速度&…

AI安全挑战与全球应对:从ComfyUI漏洞谈起

目录 引言&#xff1a;ComfyUI漏洞的警示 一、ComfyUI漏洞 1.1 漏洞类型与影响 1.2 官方处置建议 二、更广泛的AI安全挑战 2.1 快速迭代与安全滞后 2.2 数据隐私与保护 2.3 攻击手段的智能化与规模化 2.4 人才缺口与攻防不均衡 三、全球应对AI安全的努力 3.1 政府与…

天洑软件响应“链主“集结令,亮相“宁工品推“助力南京市产业链协同发展

5月27日&#xff0c;南京市召开“宁工品推”市场拓展供需对接活动暨江宁经济技术开发区专场大会&#xff0c;天洑软件响应"链主"集结令&#xff0c;亮相"宁工品推"大会现场。 大会聚集了行业内多领域杰出代表&#xff0c;通过交流讨论&#xff0c;深度剖析…

重塑企业:迈向人类、智能体与下一代组织模式

“未来的工厂只需要两名员工&#xff1a;一个人和一只狗。人的工作是喂狗&#xff0c;狗的工作是防止人碰机器。” 人工智能不再只是后台工具&#xff0c;它正逐步成为前线的协作者。当自主智能体&#xff08;智能体&#xff09;越来越能分析、优化&#xff0c;甚至代表我们做出…

时间序列噪声模型分析软件推荐与使用经验

最近在论文大修2024年投稿的一篇文章&#xff0c;大修了2轮&#xff0c;最后一次还是重新投稿&#xff0c;其中有一个问题一直被审稿人怼&#xff0c;他认为我计算时间序列的趋势的时候&#xff0c;没有考虑时间的相关性&#xff0c;即对噪声模型的估计不合理&#xff0c;会影响…

并行智算云:打破时空边界的云计算平台,助力 AI 与科研的极速前行!

一、引言 在数字化浪潮中&#xff0c;算力已然成为推动科技创新与产业变革的核心驱动力。随着人工智能&#xff08;AI&#xff09;技术的迅猛发展以及科研领域对计算需求的指数级增长&#xff0c;传统计算模式逐渐难以满足复杂任务的高效处理要求。并行智算云应运而生&#xf…

实验设计与分析(第6版,Montgomery)第5章析因设计引导5.7节思考题5.5 R语言解题

本文是实验设计与分析&#xff08;第6版&#xff0c;Montgomery著&#xff0c;傅珏生译) 第5章析因设计引导5.7节思考题5.5 R语言解题。主要涉及方差分析&#xff0c;正态假设检验&#xff0c;残差分析&#xff0c;交互作用图。 dataframe <-data.frame( wrapc(17,20,12,9,…

野火教程/SDIO工作流程/SDIO笔记

本流程是根据野火官方提供的F407源码绘制而来,可将照片另存为到自己电脑上进行观看 SDIO&#xff08;Secure Digital Input Output&#xff09;是在SD存储卡标准基础上扩展出来的一种接口标准&#xff0c;主要用于连接除了存储卡以外的输入/输出设备&#xff08;如Wi-Fi卡、蓝牙…

Vert.x学习笔记-什么是Handler

Vert.x学习笔记 在Vert.x中&#xff0c;Handler是一个核心概念&#xff0c;用于处理异步事件和回调。它是Vert.x响应式编程模型的核心组件之一&#xff0c;通过函数式接口的方式简化了异步编程的复杂性。 1. Handler的定义 Handler是一个函数式接口&#xff0c;定义如下&#…

什么是系统调用

系统调用是一种编程方式&#xff0c;计算机程序通过这种方式向执行它的操作系统内核请求服务。系统调用是程序与操作系统交互的一种方式。计算机程序在请求操作系统内核时进行系统调用。系统调用通过应用程序接口&#xff08;API&#xff09;向用户程序提供操作系统的服务。系统…

解决各个系统报错TDengine:no taos in java.library.path问题

windows 系统解决办法 在本地上安装一个TD的Windows客户端&#xff0c;注意安装的客户端版本一定要和服务端TD版本完全一致。&#xff08;或者将 C:\TDengine\driver\taos.dll 拷贝到 C:\Windows\System32\ 目录下&#xff09; 客户端各个历史版本下载链接&#xff1a;TDengin…

《100天精通Python——基础篇 2025 第22天:Python 多进程编程入门与实战详解》

目录 一、进程相关概念回顾二、多进程初体验2.1 使用multiprocessing模块创建进程2.2 使用Process子类创建进程2.3 使用进程池Pool创建进程2.4 concurrent.futures包 三、进程通信3.1 Pipe类3.2 进程队列3.2.1 队列简介3.2.2 多进程队列的使用 四、多进程优化图片下载器各个模块…

Spring boot集成milvus(spring ai)

服务器部署Milvus Run Milvus with Docker Compose (Linux) milvus版本可在docker-compose.yml中进行image修改 启动后&#xff0c;docker查看启动成功 spring boot集成milvus 参考了这篇文章 Spring AI开发RAG示例&#xff0c;理解RAG执行原理 但集成过程中遇到了一系列…

2人因经济拮据竟偷盗老房子金属门环!

2人因经济拮据竟偷盗老房子金属门环。近日,广东揭阳周田派出所连续接报多起住宅门环被盗案件,民警初步判断很可能是同一批人所为。经侦,警方成功抓获犯罪嫌疑人陈某忠、陈某晓,并查获被盗门环一批。经查,两名嫌疑人因经济拮据,专挑无人老房子盗窃。目前,案件进一步办理中…

[Dify] 如何应对明道云API数据过长带来的Token超限问题

在集成明道云与大型语言模型(LLM)如ChatGPT或本地部署的Dify时,开发者经常会面临一个核心问题:API获取的数据太长,超出LLM支持的Token数限制,导致无法直接处理。本文将深入探讨这个问题的成因,并提供几种可行的解决方案,包括分段处理、外部知识库构建等策略。 明道云AP…

周奇:藏海是庄之行生命中的光!

周奇:藏海是庄之行生命中的光。周奇在《藏海传》中饰演的庄之行与藏海关系复杂,藏海对其成长影响深远。庄之行从无忧无虑的公子到后期经历家庭变故、练武从军,角色跨度大。在这个熙熙攘攘的娱乐圈中,多少年轻的生命如繁星般闪烁,却也让人分不清哪个是珍珠,哪个是泥沙。然…

3D拟合测量水杯半径

1&#xff0c;目的。 测量水杯的半径 如图所示&#xff1a; 2&#xff0c;原理。 对 3D 点云对象 进行圆柱体拟合&#xff0c;获取拟合后的半径。 3&#xff0c;注意事项。 在Halcon中使用fit_primitives_object_model_3d进行圆柱体拟合时&#xff0c;输出的primitive_para…