代码随想录算法训练营第五十八天丨 动态规划part18

739. 每日温度

思路

首先想到的当然是暴力解法,两层for循环,把至少需要等待的天数就搜出来了。时间复杂度是O(n^2)

那么接下来在来看看使用单调栈的解法。

 什么时候用单调栈呢?

通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了。时间复杂度为O(n)。

例如本题其实就是找找到一个元素右边第一个比自己大的元素,此时就应该想到用单调栈了。

那么单调栈的原理是什么呢?为什么时间复杂度是O(n)就可以找到每一个元素的右边第一个比它大的元素位置呢?

单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素高的元素,优点是整个数组只需要遍历一次。

更直白来说,就是用一个栈来记录我们遍历过的元素,因为我们遍历数组的时候,我们不知道之前都遍历了哪些元素,以至于遍历一个元素找不到是不是之前遍历过一个更小的,所以我们需要用一个容器(这里用单调栈)来记录我们遍历过的元素。

在使用单调栈的时候首先要明确如下几点:

  • 单调栈里存放的元素是什么?

单调栈里只需要存放元素的下标 i 就可以了,如果需要使用对应的元素,直接T[i]就可以获取。

  • 单调栈里元素是递增呢? 还是递减呢?

注意以下讲解中,顺序的描述为 从栈头到栈底的顺序,因为单纯的说从左到右或者从前到后,不说栈头朝哪个方向的话,大家一定比较懵。

这里我们要使用递增循序(再强调一下是指从栈头到栈底的顺序),因为只有递增的时候,栈里要加入一个元素i的时候,才知道栈顶元素在数组中右面第一个比栈顶元素大的元素是i。

即:如果求一个元素右边第一个更大元素,单调栈就是递增的,如果求一个元素右边第一个更小元素,单调栈就是递减的。

文字描述理解起来有点费劲,接下来我画了一系列的图,来讲解单调栈的工作过程,大家再去思考,本题为什么是递增栈。

使用单调栈主要有三个判断条件。

  • 当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
  • 当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
  • 当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况

把这三种情况分析清楚了,也就理解透彻了

接下来我们用temperatures = [73, 74, 75, 71, 71, 72, 76, 73]为例来逐步分析,输出应该是 [1, 1, 4, 2, 1, 1, 0, 0]。


首先先将第一个遍历元素加入单调栈

739.每日温度1


加入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]。

739.每日温度2


加入T[2],同理,T[1]弹出

739.每日温度3


加入T[3],T[3] < T[2] (当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况),加T[3]加入单调栈。

739.每日温度4


加入T[4],T[4] == T[3] (当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况),此时依然要加入栈,不用计算距离,因为我们要求的是右面第一个大于本元素的位置,而不是大于等于!

739.每日温度5


加入T[5],T[5] > T[4] (当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况),将T[4]弹出,同时计算距离,更新result 

739.每日温度6


T[4]弹出之后, T[5] > T[3] (当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况),将T[3]继续弹出,同时计算距离,更新result 

739.每日温度7


直到发现T[5]小于T[st.top()],终止弹出,将T[5]加入单调栈

739.每日温度8


加入T[6],同理,需要将栈里的T[5],T[2]弹出

739.每日温度9


同理,继续弹出

739.每日温度10


此时栈里只剩下了T[6]

739.每日温度11


加入T[7], T[7] < T[6] 直接入栈,这就是最后的情况,result数组也更新完了。

739.每日温度12

此时可能就疑惑了,那result[6] , result[7]怎么没更新啊,元素也一直在栈里。

其实定义result数组的时候,就应该直接初始化为0,如果result没有更新,说明这个元素右面没有更大的了,也就是为0。

以上在图解的时候,已经把,这三种情况都做了详细的分析。

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

通过以上过程,大家可以自己再模拟一遍,就会发现:只有单调栈递增(从栈口到栈底顺序),就是求右边第一个比自己大的,单调栈递减的话,就是求右边第一个比自己小的。

代码如下:

class Solution {
    public int[] dailyTemperatures(int[] temperatures) {
        int[] answer = new int[temperatures.length];
        //小的话一直压栈记录,大的话就下表相减求距离
        /**
         如果当前遍历的元素 大于栈顶元素,表示 栈顶元素的 右边的最大的元素就是 当前遍历的元素,
         所以弹出 栈顶元素,并记录
         如果栈不空的话,还要考虑新的栈顶与当前元素的大小关系
         否则的话,可以直接入栈。
         注意,单调栈里 加入的元素是 下标。
         */
        Stack<Integer> st = new Stack<>();
        st.push(0);
        for (int i = 1; i < temperatures.length; i++) {
            if (temperatures[i] > temperatures[st.peek()]){
                //如果当前元素大于栈顶元素
                //遍历栈,如果碰到当前元素小于等于栈顶元素时,将当前元素的下标放入栈中
                //如果当前元素大于栈顶元素,栈顶元素为下标的值为 i- st.pop
                while (!st.isEmpty() && temperatures[i] > temperatures[st.peek()]){
                    if (temperatures[i] > temperatures[st.peek()]){
                        int stIndex = st.pop();
                        answer[stIndex] = i - stIndex;
                    }
                }
                st.push(i);
            }else {
                //当前元素小于等于栈顶元素
                st.push(i);
            }
        }
        return answer;
    }
}

496.下一个更大元素 I

思路

这题秒了基本没看卡哥的题解,但思路基本也是与卡哥的一致。但需要注意的细节点是,每次 i 遍历完之后需要对栈stack 进行清空处理,防止本次遗留元素影响到下一次层的循环中。

并且我将结果数组 res[] 均初始化为了-1,具体思路见代码:

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        //nums1 是 nums2 的子集
        int[] res = new int[nums1.length];
        Arrays.fill(res,-1);
        Stack<Integer> st = new Stack<>();

        //找出num1[i] == num2[j]的j 值
        for (int i = 0; i < nums1.length; i++) {
            for (int j = 0; j < nums2.length; j++) {
                if (nums1[i] == nums2[j]){
                    //确定 nums2[j] 的 下一个更大元素
                    st.push(j);
                }
                if (!st.isEmpty() && nums2[j] > nums2[st.peek()]){
                    res[i] = nums2[j];
                    st.pop();
                }
            }
            st.clear();
        }
        return res;
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

智能配电房管理系统

智能配电房管理系统依托电易云-智慧电力物联网&#xff0c;是一种集成了互联网、物联网、大数据、人工智能等先进技术的管理系统&#xff0c;专门用于配电房的智能化管理和运维。以下是智能配电房管理系统的主要功能和特点&#xff1a; 一、主要功能&#xff1a; 实时监测与数据…

SQL 的 AND、OR 和 NOT 运算符:条件筛选的高级用法

AND 运算符 SQL的AND运算符用于根据多个条件筛选记录&#xff0c;确保所有条件都为TRUE才返回记录。下面是AND运算符的基本语法&#xff1a; SELECT column1, column2, ... FROM table_name WHERE condition1 AND condition2 AND condition3 ...;column1, column2,等是您要选…

Go 语言数组基础教程 - 数组的声明、初始化和使用方法

数组用于在单个变量中存储相同类型的多个值&#xff0c;而不是为每个值声明单独的变量。 声明数组 在Go中&#xff0c;有两种声明数组的方式&#xff1a; 使用var关键字&#xff1a; 语法 var array_name [length]datatype{values} // 这里定义了长度 或者 var array_n…

Typora for Mac:打造全新文本编辑体验

Typora for Mac是一款与众不同的文本编辑器&#xff0c;它不仅拥有直观易用的界面&#xff0c;还融合了Markdown语法和富文本编辑的功能&#xff0c;为用户带来了前所未有的写作和编辑体验。 一、简洁明了的界面设计 Typora for Mac的界面简洁明了&#xff0c;让用户可以专注…

【科技素养】蓝桥杯STEMA 科技素养组模拟练习试卷F

1、常见的加密算法可以分为对称加密算法和非对称加密算法&#xff0c;以下关于它们的描述正确的是 A、AES是一种常见的非对称加密算法 B、凯撒密码是一种非对称加密 C、非对称加密算法的解密使用的秘钥与加密不同 D、对称加密算法无法被暴力破解 答案&#xff1a;C 2、12根…

我为什么开始写技术博客

今天没有技术文章&#xff0c;只是想聊聊认真做CSDN和公众号以来的一些感想。 1.为什么开启技术分享 我不算是一个聪明的人&#xff0c;没有过目不忘的本事&#xff0c;所以从工作开始就养成了做笔记的习惯&#xff1b; 最开始15、16年做模型开发&#xff0c;那时候环境其实就…

LINUX入门篇【6】----第一个LINUX小程序---进度条及相关知识讲解

前言&#xff1a; 本篇我们将开始尝试构建我们的第一个LINUX的小程序----进度条作为一个十分常见的程序&#xff0c;在我们之后的工程实践中也是需要多次运用&#xff0c;但是介于我们目前还没有去学习网络等方面的知识&#xff0c;没法独立的去利用程序去下载一个真正的程序&…

Humanoid Robotics Whole Body Control(WBC)全身控制

系列文章目录 文章目录 系列文章目录前言一、ROS —— 什么是全身控制&#xff1f;二、IEEE - RAS三、维也纳工业大学 —— 自动化与控制研究所&#xff08;ACIN&#xff09;四、IIt&#xff08;意大利技术研究院&#xff09; 前言 谷歌的几种解释 一、ROS —— 什么是全身控制…

PPT基础入门

目录 相关设置快捷键shift 快捷键Ctrl 快捷键Ctrl Shift 组合快捷键快捷键总结 相关设置 设置撤回次数 自动保存 图片压缩 字体嵌入&#xff1a;目的是在不同的电脑上保留已经设置好的字体 多格式导出 &#xff08;1&#xff09;可以导出PDF &#xff08;2&#xff09;可以导…

简单线性回归函数

简单线性回归函数 定义术语理解简单线性回归例子 定义 线性回归&#xff1a;利用线性回归方程中最小平方函数对一个或多个自变量和因变量之间关系进行建模的一个回归分析。该建模的目标为找到各个系数的最佳值让预测误差最小 简单线性回归&#xff1a;只有一个自变量的线性回…

2023.11.18 每日一题(AI自生成应用)【C++】【Python】【Java】【Go】 动态路径分析

目录 一、编程挑战&#xff1a;动态时间序列分析 实际应用&#xff1a; 实现提示&#xff1a; 二、实现 1. C 2. Python 3. JAVA 4. Go 一、编程挑战&#xff1a;动态时间序列分析 问题描述&#xff1a; 假设你是一名软件工程师&#xff0c;需要开发一个应用来分析和预…

贝茄莱BR AS实时数据采集功能

实时数据采集功能在PLC系统调试过程中&#xff0c;有助于调试人员对变量变化进行监测&#xff0c;通过波形对比&#xff0c;反应不同变量间的相互作用。该测试目的在于验证贝加莱系统组态软件的实时数据采集功能。 贝加莱系统组态软件提供Trace功能&#xff0c;连接PLC&#x…

如何在远程协同视频会议中确保安全性?

随着远程工作的普及&#xff0c;远程协同视频会议已成为企业和团队之间进行交流和协作的重要工具。与此同时&#xff0c;会议中的安全性问题也日益凸显。本文将介绍如何在远程协同视频会议中确保安全性&#xff0c;主要包括以下方面&#xff1a; 1、内网部署 将会议服务器部署…

基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(五)

公共字段自动填充 1.1 问题分析1.2 实现思路1.3 代码开发1.3.1 步骤一1.3.2 步骤二1.3.3 步骤三 1.4 功能测试 1.1 问题分析 在前面我们已经完成了后台系统的员工管理功能和菜品分类功能的开发&#xff0c;在新增员工或者新增菜品分类时需要设置创建时间、创建人、修改时间、修…

【开源】基于Vue和SpringBoot的婚恋交友网站

项目编号&#xff1a; S 057 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S057&#xff0c;文末获取源码。} 项目编号&#xff1a;S057&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 会员管理模块2.3 新…

腾讯云轻量4核8G12M带宽服务器租用价格和S5实例报价

腾讯云4核8G服务器优惠价格表&#xff0c;云服务器CVM标准型S5实例4核8G配置价格15个月1437.3元&#xff0c;5年6490.44元&#xff0c;轻量应用服务器4核8G12M带宽一年446元、529元15个月&#xff0c;阿腾云atengyun.com分享腾讯云4核8G服务器详细配置、优惠价格及限制条件&…

llvm源码windows编译

1.克隆llvm源码: git clone --config core.autocrlf=false https://github.com/llvm/llvm-project.git 2.创建build文件夹并生成makefile 生成前置条件: cmake ,ninja,python3要先安装 cmake -S llvm -B build -G Ninja -DCMAKE_BUILD_TYPE=Release 生成成功 3.编译 进…

场景交互与场景漫游-场景漫游器(6)

场景漫游 在浏览整个三维场景时&#xff0c;矩阵变换是非常关键的&#xff0c;通过适当的矩阵变换可以获得各种移动或者渲染效果。因此&#xff0c;在编写自己的场景漫游操作器时&#xff0c;如何作出符合逻辑的矩阵操作器是非常重要的&#xff0c;但这对初学者来说还是有一定难…

Flutter笔记:桌面端应用多窗口管理方案

Flutter笔记 桌面端应用多窗口管理方案 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/134468587 【简介…

Excel 文件比较工具 xlCompare 11.01 Crack

比较两个 Excel 文件之间的差异 xlCompare. xlCompare.com 是性能最佳的 Excel diff 工具&#xff0c;用于比较两个 Excel 文件或工作表并在线突出显示差异。xlCompare 包括免费的在线 Excel 和 CSV 文件比较服务以及用于比较和合并 Excel 文件的强大桌面工具。如果您想在线了…