代码随想录算法训练营第48天 | LeetCode739.每日温度、 LeetCode496.下一个更大元素I、 LeetCode503.下一个更大元素II

 目录

LeetCode739.每日温度

LeetCode496.下一个更大元素I 

1. 暴力解法

2. 单调栈法

LeetCode503.下一个更大元素II


LeetCode739.每日温度

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

思路:这里涉及到了单调栈的问题。

当我们遇到要求某个元素最左边或者最右边第一个大于或小于该元素的值时,就可以考虑使用单调栈。

单调栈的本质就是拿空间换时间,使用一个栈来记录之前已经遍历过的元素,这样可以依据已经遍历过的元素来做出判断,得出结果。所以它的时间复杂度通常是O(n)。

单调栈又分为递增栈和递减栈。

所谓递增栈就是从栈顶到栈底的顺序,元素呈递增的状态,通常是用于求某元素右边第一个比其大的元素的值;而递减栈也是从栈顶到栈底的顺序,元素呈递减的状态,通常是用于求某元素右边第一个比其小的元素的值。

这里的每日温度就是单调栈比较经典的题目。

首先如果是暴力的解法,那么就是两层循环,一层记录当前待记录的元素,另一层开始遍历该元素后面的元素,遇到第一个比它大的就记录并停止循环。这个思路比较好想,当时毕竟是暴力解法,容易超限。

    vector<int> dailyTemperatures(vector<int>& temperatures) {vector<int> answer(temperatures.size(), 0);for(int i = 0; i < temperatures.size(); i ++){int max_index = i;for(int j = i + 1; j < temperatures.size(); j ++){if(temperatures[j] > temperatures[max_index]){max_index = j;break;//碰到大的值即可停止遍历了}}if(max_index != i) answer[i] = max_index - i;}return answer;   }

时间复杂度:O(n^2)

空间复杂度:O(n)

所以这里我们采用单调栈,这里采用的是递增栈,使用一个栈来记录已经遍历过的元素。

当栈元素不为空,并且存在元素大于栈顶元素时,开始记录,注意这里是一个while循环,因为遇到一个较大值可能不止是一个元素的右边第一个最大值,可能存在多个,所以要多次比较,并且每次比较完成即可pop掉相应元素。

这里详细考虑了三种情况,遍历元素小于栈顶元素、遍历元素等于栈顶元素以及遍历元素大于栈顶元素。

    vector<int> dailyTemperatures(vector<int>& temperatures) {vector<int> answer(temperatures.size(), 0);stack<int> max_index;//递增栈记录下标索引max_index.push(0);for(int i = 1; i < temperatures.size(); i ++){if(temperatures[i] < temperatures[max_index.top()]){//遍历元素小于栈里面的元素max_index.push(i);}else if(temperatures[i] == temperatures[max_index.top()]){//遍历元素等于栈里面的元素max_index.push(i);}else{//遍历元素大于栈里面的元素while(!max_index.empty() && temperatures[i] > temperatures[max_index.top()]){answer[max_index.top()] = i - max_index.top();max_index.pop();}max_index.push(i);}}return answer;   }

其实可以发现上面三种情况具有共同点,所以可以简化为下面的代码。

    vector<int> dailyTemperatures(vector<int>& temperatures) {vector<int> answer(temperatures.size(), 0);stack<int> max_index;//记录下标索引max_index.push(0);for(int i = 1; i < temperatures.size(); i ++){//当栈不为空并且temperatures[i]>temperatures[max_index.top()],开始计算下次最高温度出现在几天后while(!max_index.empty() && temperatures[i] > temperatures[max_index.top()]){int index = max_index.top(); max_index.pop();answer[index] = i - index;}max_index.push(i);}return answer;   }

时间复杂度:O(n)

空间复杂度:O(n)

LeetCode496.下一个更大元素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] 是如上所述的 下一个更大元素

思路:这里有两种思路,可以尝试暴力解法以及单调栈方法。

1. 暴力解法

这里有两层循环,外层循环是nums1中的元素,内层循环是nums2中的元素,在nums2中找到nums1[i]的元素,然后开始向后遍历,找到第一个最大元素值,记录下来即可。

    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {vector<int> ans(nums1.size(), -1);//记录最终结果for(int i = 0; i < nums1.size(); i ++){for(int j = 0; j < nums2.size(); j ++){if(nums1[i] == nums2[j]){//在nums2中找到了nums1中的元素int index = j ++;//开始找寻在这个相同元素后面是否存在比这个元素大的值while(index < nums2.size()){if(nums2[index] > nums1[i]){ans[i] = nums2[index];break;}else{index ++;}}}}}return ans;}

时间复杂度:O(n^2)

空间复杂度:O(n)

2. 单调栈法

我们可以借鉴一下每日温度的思路。

这里同样是要判断遍历元素是否大于栈顶元素。但是在赋值之前还要判断遍历元素是否在nums1中出现过,因为主体循环肯定是在nums2进行的。

这里知道nums1、nums2中的元素是不相等的,所以想要判断元素是否存在在nums1中,那么可以使用哈希方法进行映射,这里选择unordered_map对值和下标进行映射。

所以在遇到遍历元素大于栈顶元素的时候,需要判断nums1中是否出现过这个栈顶元素,因为如果出现过,那么这个遍历元素就是它在nums2中第一个大于它的值,就可以进行记录了。

这里需要注意pop函数的位置,一定是在条件判断之外的,否则会造成死循环。

下面是详细的情况讨论的代码。

    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {vector<int> ans(nums1.size(), -1);//记录最终结果unordered_map<int, int> mp;for(int i = 0; i < nums1.size(); i ++){//将nums1中的元素映射到mp中mp[nums1[i]] = i;}stack<int> st;//递增栈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()]){if(mp.find(nums2[st.top()]) != mp.end()){//首先在遇到大于的情况下,查看在nums1中是否出现过该元素//如果出现过则根据映射找到在nums1中的索引下标,然后在ans对应的下标位置为这个大于的值nums2[i]ans[mp[nums2[st.top()]]] = nums2[i];}st.pop();}st.push(i);}}return ans;}

下面是进行精简后的代码。

    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {vector<int> ans(nums1.size(), -1);unordered_map<int, int> mp;for(int i = 0; i < nums1.size(); i ++){mp[nums1[i]] = i;}stack<int> st;st.push(0);for(int i = 1; i < nums2.size(); i ++){while(!st.empty() && nums2[i] > nums2[st.top()]){if(mp.find(nums2[st.top()]) != mp.end()){ans[mp[nums2[st.top()]]] = nums2[i];}st.pop();//这里的pop需要放在条件判断之外,否则会出现死循环,最后超限}st.push(i);}return ans;}

时间复杂度:O(n)

空间复杂度:O(n)

LeetCode503.下一个更大元素II

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

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

思路:这道题相对于上一道题来说比较简单,跟每日温度比起来相似度极高。

这里唯一的不同在于nums是一个循环数组。

处理循环数组可以有两种思路:

第一种是将两个nums拼接在一起,然后求出所有元素的下一个更大元素的ans,最后得到结果后将ans裁剪为一半(大小为nums.size()),返回即可。但是这里涉及到扩充以及resize操作,增加了一些没必要的操作,所以不是首选,这里只是提供一种思路。

第二种是采用循环遍历,将遍历次数设置为2*nums.size()(当然遍历次数为2*nums.size()-2即能满足题意了),统计元素的下一个更大的元素,其他的跟每日温度其实基本上就是一样的了,最后当循环结束即可得到最终结果。

    vector<int> nextGreaterElements(vector<int>& nums) {int size = nums.size();//记录下nums的大小vector<int> ans(size, -1);//记录最终结果stack<int> st;st.push(0);for(int i = 1; i < 2 * size - 1; i ++){//这里将循环的次数变成了2*size-2,足够绕一圈了while(!st.empty() && nums[i % size] > nums[st.top()]){ans[st.top()] = nums[i % size];st.pop();}st.push(i % size);}return ans;}

时间复杂度:O(n)

空间复杂度:O(n)

感谢你的阅读,希望我的文章能够给你帮助,如果有帮助,麻烦点赞加收藏,或者点点关注,非常感谢。

如果有什么问题欢迎评论区讨论!

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

2024最新版mysql数据库表的查询操作-总结

序言 1、MySQL表操作(创建表&#xff0c;查询表结构&#xff0c;更改表字段等)&#xff0c; 2、MySQL的数据类型(CHAR、VARCHAR、BLOB,等)&#xff0c; 本节比较重要&#xff0c;对数据表数据进行查询操作&#xff0c;其中可能大家不熟悉的就对于INNER JOIN(内连接)、LEFT JOIN…

Learn ComputeShader 15 Grass

1.Using Blender to create a single grass clump 首先blender与unity的坐标轴不同&#xff0c;z轴向上&#xff0c;不是y轴 通过小键盘的数字键可以快速切换视图&#xff0c;选中物体以后按下小键盘的点可以将物体聚焦于屏幕中心 首先我们创建一个平面&#xff0c;宽度为0.2…

SpringBoot中使用EasyExcel并行导出多个excel文件并压缩zip后下载

❃博主首页 &#xff1a; 「码到三十五」 &#xff0c;同名公众号 :「码到三十五」&#xff0c;wx号 : 「liwu0213」 ☠博主专栏 &#xff1a; <mysql高手> <elasticsearch高手> <源码解读> <java核心> <面试攻关> ♝博主的话 &#xff1a…

SysML图例-农业无人机

DDD领域驱动设计批评文集>> 《软件方法》强化自测题集>> 《软件方法》各章合集>>

dll修复工具4DDiG DLL Fixer,解决电脑dll丢失问题

4DDiG DLL Fixer是一款专业的DLL修复工具&#xff0c;旨在解决Windows系统中各种DLL相关问题。该工具能够快速全面地扫描计算机&#xff0c;检测并修复导致程序功能异常的DLL错误。它支持一键式操作&#xff0c;自动扫描、识别和替换缺失或损坏的DLL文件&#xff0c;从而帮助用…

推荐3款AIai论文大纲一键生成文献,精选整理!

在当前的学术写作环境中&#xff0c;AI论文大纲生成工具已经成为许多学者和学生的重要助手。这些工具不仅能够快速生成高质量的论文大纲&#xff0c;还能提供内容填充、文献引用和查重修改等全方位的服务。以下是三款值得推荐的AI论文大纲一键生成文献工具&#xff1a;千笔-AIP…

爬虫--翻页tips

免责声明&#xff1a;本文仅做分享&#xff01; 伪线程 from DrissionPage import ChromiumPage import timepage ChromiumPage() page.get("https://you.ctrip.com/sight/taian746.html") # 初始化 第0页 index_page 0# 翻页点击函数 sleep def page_turn():page…

C/C++语言基础--从C到C++的不同(下),15个部分说明C与C++的不同

本专栏目的 更新C/C的基础语法&#xff0c;包括C的一些新特性 前言 1-10在上篇C/C语言基础–从C到C的不同(上&#xff09;&#xff1b;当然C和C的不同还有很多&#xff0c;本人暂时只总结这些&#xff0c;其他的慢慢更新&#xff1b;上一篇C/C语言基础–从C到C的不同(上&…

node.js 中的进程和线程工作原理

本文所有的代码均基于 node.js 14 LTS 版本分析 概念 进程是对正在运行中的程序的一个抽象&#xff0c;是系统进行资源分配和调度的基本单位&#xff0c;操作系统的其他所有内容都是围绕着进程展开的 线程是操作系统能够进行运算调度的最小单位&#xff0c;其是进程中的一个执…

康养小站:长者舒缓疼痛的港湾

【导语】在老龄化日益加剧的当下&#xff0c;如何关爱和照顾好长者&#xff0c;成为社会关注的焦点。近日&#xff0c;笔者走进深圳宝安区一家专注于长者康养的社区小站&#xff0c;探访它如何帮助长者缓解疼痛&#xff0c;提高生活质量。 随着我国人口老龄化问题日益显著&…

算法:30.串联所有单词的子串

题目 链接&#xff1a;leetcode链接 思路分析&#xff08;滑动窗口&#xff09; 这道题目类似寻找异位词的题目&#xff0c;我认为是寻找异位词的升级版 传送门:寻找异位词 为什么说像呢&#xff1f; 注意&#xff1a;这道题目中words数组里面的字符串长度都是相同的&…

[JAVA]介绍怎样在Java中通过字节字符流实现文件读取与写入

一&#xff0c;初识File类及其常用方法 File类是java.io包下代表与平台无关的文件和目录&#xff0c;程序中操作文件和目录&#xff0c;都可以通过File类来完成。 通过这个File对象&#xff0c;可以进行一系列与文件相关的操作&#xff0c;比如判断文件是否存在&#xff0c;获…

Java毕业设计 基于SpringBoot和Vue药店管理系统

Java毕业设计 基于SpringBoot和Vue药店管理系统 这篇博文将介绍一个基于SpringBoot框架和Vue开发的药店管理系统&#xff0c;适合用于Java毕业设计。 功能介绍 首页 图片轮播 登录 注册 药品信息 药品详情 评论 收藏 购买 添加到购物车 用药指南 公告资讯 购物车 …

在深圳停车场我居然能看到很漂亮的瓦房

石岩街道在宝安确实是小透明哈&#xff0c;从市区搬到石岩快4年了&#xff0c;确实这里的建筑特别像老家的感觉&#xff0c;马路很狭窄。如果是开车的话&#xff0c;我是不会进入罗租大道来着&#xff0c;人车太复杂。由于上屋社区适合儿童的室内场所太少了&#xff0c;石岩这块…

python之模块和包的导入与使用,pip的使用(13)

文章目录 1、模块1.1 模块的分类1.1.1 内置模块1.1.2 第三方模块&#xff08;比较重要&#xff09;1.1.3 自定义模块 1.2 模块的导入1.2.1 单个模块的导入1.2.2 同时导入多个模块1.2.3 模块导入规范1.2.4 给导入的模块取别名1.2.5 同时导入模块和名字1.2.6 给导入的名字取别名扩…

【Python机器学习】序列到序列建模——使用序列到序列网络构建一个聊天机器人

为了寻聊天机器人&#xff0c;下面使用康奈尔电影对话语料库训练一个序列到序列的网络来“适当的”湖大问题或语句。以下聊天机器人示例采用的是Keras blog中的序列到序列的示例。 为训练准备语料库 首先&#xff0c;需要加载语料库并从中生成训练集&#xff0c;训练数据将决…

【刷题】Day5--数字在升序数组中出现的次数

Hi! 今日份刷题~ 数字在升序数组中出现的次数_牛客题霸_牛客网 我感觉题目简单&#xff0c;我的解答也很简单&#xff0c;二分法遗忘&#xff0c;有时间复习一下尝试新的解法。 /*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的…

electron-updater实现electron全量版本更新

在 Electron 应用中使用 electron-updater 来实现自动更新功能时&#xff0c;通常你会在一个专门的模块或文件中管理更新逻辑。如果你想要使用 ES6 的 import 语法来引入 electron-updater&#xff0c;你需要确保你的项目已经配置好了支持 ES6 模块的构建工具&#xff08;如 We…

系统指标优化 Stream流 API使用及源码分析

java 8 Stream流 api 使用 &#xff08;1&#xff09;集合筛选&#xff0c;为什么此处要使用 Collectors.toMap 加 Function.identity() 的方式&#xff0c;而不使用简单的filter过滤呢&#xff1f; Stream流 API 代码: collect(Collectors.toMap(GoldTagResponse::getValue, …

C语言深入理解指针(一)

目录 内存和地址内存编址的理解 指针变量和地址取地址操作符&#xff08;&&#xff09;指针变量和解引用操作符&#xff08;*&#xff09;指针变量如何拆解指针类型解引用操作符 指针变量的大小 指针变量类型的意义指针的解引用指针-整数 const修饰指针const修饰变量const修…