Studying-代码随想录训练营day48| 739. 每日温度、496.下一个更大元素 I、503.下一个更大元素II

news2024/9/20 4:59:00

第48天,单调栈part01,栈的特殊应用场所!编程语言:C++

目录

739. 每日温度

496.下一个更大元素 I

503.下一个更大元素II

总结:


739. 每日温度

文档讲解:代码随想录每日温度

视频讲解:手撕每日温度

题目: 739. 每日温度 - 力扣(LeetCode)

学习:本题是单调栈的第一道题。本题直观的解决办法是采用双层循环,一层遍历当前元素,一层遍历后续元素并找到比当前元素大的值,以此来进行求解,时间复杂度为O(n^2)。

但本题可以采用单调栈的方式,降低时间复杂度,单调栈的本质是空间换时间,因为在遍历的过程中需要用一个栈来记录右边第一个比当前元素高的元素,优点是整个数组只需要遍历一次。

单调栈的主要使用场景就是寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置。最终实现的复杂度为O(n)。注意单调栈中存放的应该是遍历过的元素。

在每一题使用单调栈前我们需要注意两点:1. 单调栈里存放的元素是什么?2. 单调栈里的元素是递增呢?还是递减呢?(这个的递增递减是从栈顶往栈底看的)

在本题中,我们可以在单调栈中存放元素下标,以方便我们记录下一个更大元素的位置。同时我们要找的是比当前元素大的元素,因此我们单调栈应该采取递增的方式,比当前元素小,就加入栈中。

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

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

代码:我们可以通过代码,直观感受单调栈的使用:

//时间复杂度O(n)
//空间复杂度O(n)
class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        vector<int> result(temperatures.size(), 0); //返回的答案数组
        stack<int> st; //单调栈:适合查找左边或者右边,第一个比当前元素大或者小的元素
        st.push(0); //放入第一个元素,注意放入的应该是下标
        for(int i = 1; i < temperatures.size(); i++) {
            if(temperatures[i] <= temperatures[st.top()]) { //如果当前元素比栈顶元素小,则加入栈中
                st.push(i);
            }
            else { //如果当前元素比栈顶元素大,则要开始弹出
                while(!st.empty() && temperatures[i] > temperatures[st.top()]) { //注意这个地方必须要用循环
                    result[st.top()] = i - st.top();
                    st.pop();
                }
                st.push(i);
            }
        }
        return result;
    }
};

代码:我们也可以将上述代码简化,因为无论如何我们都会有st.push(i)这一步存在。

//时间复杂度O(n)
//空间复杂度O(n)
class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        vector<int> result(temperatures.size(), 0); //返回的答案数组
        stack<int> st; //单调栈:适合查找左边或者右边,第一个比当前元素大或者小的元素
        st.push(0); //放入第一个元素,注意放入的应该是下标
        for(int i = 1; i < temperatures.size(); i++) {
            while(!st.empty() && temperatures[i] > temperatures[st.top()]) {
                result[st.top()] = i - st.top();
                st.pop();
            }
            st.push(i);
        }
        return result;
    }
};

496.下一个更大元素 I

文档讲解:代码随想录下一个更大元素I

视频讲解:手撕下一个更大元素I

题目:496. 下一个更大元素 I - 力扣(LeetCode)

学习:本题也是找下一个更大元素,解题的核心思路是一样的。只不过本题还需要解决一个问题,就是nums1到nums2的映射问题,解决这个问题我们可以采用哈希表的方法,把nums1的元素加入到哈希表中,之后在nums2内通过查找哈希表的方式,进行相关元素的赋值。

这里要注意,我们遍历的一定是nums2,因为我们要找的是nums2中下一个更大元素。

同时本题单调栈中,我们可以存取下标(然后通过下标来找到对应元素),也可以存取元素,因为本题我们需要找的是最大元素,而不是下标位置。

代码:存取下标的方式

//时间复杂度O(n)
//空间复杂度O(n)
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; // key:下标元素,value:下标
        for (int i = 0; i < nums1.size(); i++) {
            umap[nums1[i]] = i;
        }
        st.push(0);
        for (int i = 1; i < nums2.size(); i++) {
            while (!st.empty() && nums2[i] > nums2[st.top()]) {
                if (umap.count(nums2[st.top()]) > 0) { // 看map里是否存在这个元素
                    int index = umap[nums2[st.top()]]; // 根据map找到nums2[st.top()] 在 nums1中的下标
                    result[index] = nums2[i];
                }
                st.pop();
            }
            st.push(i);
        }
        return result;
    }
};

代码:存取元素的方式

class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        vector<int> result(nums1.size(), -1); //记录答案
        unordered_map<int, int> umap; //使用哈希表构建nums1和nums2的关系
        for(int i = 0; i < nums1.size(); i++) {
            umap[nums1[i]] = i;
        }

        stack<int> st; //单调栈,栈顶到栈底从小到大
        st.push(nums2[0]);

        for(int i = 0; i < nums2.size(); i++) {
            while(!st.empty() && nums2[i] > st.top()) {
                if(umap.count(st.top()) != 0) {
                    result[umap[st.top()]] = nums2[i];
                }
                st.pop();
            }
            st.push(nums2[i]);
        }
        return result;
    }
};

503.下一个更大元素II

文档讲解:代码随想录下一个元素更大II

视频讲解:手撕下一个元素更大II

题目: 503. 下一个更大元素 II - 力扣(LeetCode)

学习:本题相较于上一题其实更为简单,本题我们主要需要解决的是循环数组的问题。关于这个问题有两个求解办法。

1.通过扩展数组的方式,实现循环数组。也就是将两个nums数组拼接到一起,然后使用单调栈计算出每一个元素的下一个最大值,最后再把结果集即result数组resize到原数组大小就可以了。

代码:

//时间复杂度O(n)
//空间复杂度O(2n)
class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        // 拼接一个新的nums
        vector<int> nums1(nums.begin(), nums.end());
        nums.insert(nums.end(), nums1.begin(), nums1.end());
        // 用新的nums大小来初始化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到原数组大小
        result.resize(nums.size() / 2);
        return result;
    }
};

2.也可以采用取余的方式进行循环数组求解,这个方式也是目前比较好的方式,后面碰到循环数组,都可以采取取余的方式进行求解。本质上就是通过取余来遍历数组两次。

代码:

//时间复杂度O(n)
//空间复杂度O(n)
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 = 0; i < nums.size() * 2; i++) { //使用取余的方法,模拟环形数组
            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.coloradmin.cn/o/1962999.html

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

相关文章

龙迅LT8711GX 高性能Type-C/DP1.4/EDP转HDMI2.1转换器,内置MCU,支持8K30HZ

龙迅LT8711GX描述&#xff1a; LT8711GX是一个高性能的Type-C/DP1.4a到HDMI2.1转换器&#xff0c;设计用于连接一个USB Type-C源或一个DP1.4a源到一个HDMI2.1接收器。LT8711GX集成了一个与DP1.4a兼容的接收器&#xff0c;和一个与HDMI2.1兼容的发射机。此外&#xff0c;还包括…

【Kubernetes】二进制部署k8s集群(中)之cni网络插件flannel和calico

&#xff01;&#xff01;&#xff01;继续上一篇实验部署&#xff01;&#xff01;&#xff01; 目录 一.k8s的三种网络模式 1.Pod 内容器与容器之间的通信 2.同一个 Node 内 Pod 之间的通信 3.不同 Node 上 Pod 之间的通信 二.k8s的三种接口 三.Flannel 网络插件 1.U…

爆“卷”的AI视频,大厂向左,创企向右

文&#xff5c;白 鸽 编&#xff5c;王一粟 “生成的人物一转身就变成老外&#xff0c;怎么解决呢&#xff1f;” “没有办法&#xff0c;10s中动作大的&#xff0c;人物一致性有问题&#xff0c;只能抽卡&#xff0c;多刷几个&#xff0c;选择一个变化不大的。” 在一个以…

iOS开发过程中经常遇到的GCD的一些问题

目录 前言 1.performSelector相关的一个问题 2.一个崩溃问题 前言 记录GCD使用过程中遇到的一个小问题。 1.performSelector相关的一个问题 输入下面代码的打印结果&#xff1a; - (void)viewDidLoad {[super viewDidLoad];[self gcdDemos01]; } - (void)gcdDemos01{dispa…

语音识别 音码同步声纹分切系统 上海添力

目前主流的语音识别系统&#xff0c;是将语音识别成汉字&#xff0c;会有以下几个问题&#xff1a; 一、一旦识别成汉字&#xff0c;对应语音原有的声音属性都会丢失。在后期的人工智能应用中&#xff0c;仍然需要对识别出的汉字进行人工标注&#xff0c;以区别相同词语的不同情…

【机器学习sklearn实战】线性回归

一 基础概念 广义线性模型&#xff08;2&#xff09;线性回归 【机器学习】一文看尽 Linear Regression 线性回归 二 步骤 使用sklearn中的库&#xff0c;一般使用线性回归器 首先&#xff0c;导入包&#xff1a; from sklearn.linear_model import LinearRegression创建模…

PHP回收废品平台系统小程序源码

&#x1f30d;绿色行动&#xff0c;从“回收废品平台系统”开始&#xff01;&#x1f69a; &#x1f6aa;【家门口的环保站&#xff0c;废品不再无处安放】 你是否曾为家里的旧报纸、空瓶子、废旧电器等废品头疼不已&#xff0c;不知该如何处理&#xff1f;现在&#xff0c;“…

java-数据结构与算法-02-数据结构-07-优先队列

1. 概念 队列是一种先进先出的结构&#xff0c;但是有些时候&#xff0c;要操作的数据带有优先级&#xff0c;一般出队时&#xff0c;优先级较高的元素先出队&#xff0c;这种数据结构就叫做优先级队列。 比如&#xff1a;你在打音游的时候&#xff0c;你的朋友给你打了个电话…

安全基础学习-CRC理解与计算

由于一些任务要求需要了解CRC校验&#xff0c;于是来学习一下。 新人学习&#xff0c;大佬绕路。 前言 CRC即循环冗余校验码&#xff1a;是数据通信领域中最常用的一种查错校验码&#xff0c;其特征是信息字段和校验字段的长度可以任意选定。循环冗余检查&#xff08;CRC&…

《计算机工程与应用》投稿经验

目录 前言期刊简介详细过程第1步&#xff1a;网站投稿第2步&#xff1a;交审稿费第3步&#xff1a;等外审第4步&#xff1a;返修所需材料描述 第5步&#xff1a;外审/复审/录用第6步&#xff1a;录用 前言 记录下投《计算机工程与应用》的正刊投稿经验。 整理了一些投稿期间常…

【C#】 使用GDI+获取两个多边形区域相交、非相交区域

一、使用GDI获取两个多边形区域相交、非相交区域 在 C# 中使用 GDI&#xff08;Graphics Device Interface Plus&#xff09;处理图形时&#xff0c;你可以使用 System.Drawing 和 System.Drawing.Drawing2D 命名空间中的类来操作区域&#xff08;Region&#xff09;。下面是一…

取图小程序搭建教程,达人+流量主+会员快速部署上线指南

目录 一、取图小程序是什么&#xff1f; 二、搭建教程 &#xff08;一&#xff09;前期准备 &#xff08;二&#xff09;开发环境搭建 &#xff08;三&#xff09;部署与上线 三、部分代码展示 一、取图小程序是什么&#xff1f; 取图小程序以其多元化的功能而著称&…

抖音本地生活城市代理保证金多少?最新标准出炉!

随着本地生活的前景和潜力不断显现&#xff0c;抖音本地生活城市代理的申请热度不断攀升&#xff0c;并逐渐成为众多创业者入局本地生活的首要选择&#xff0c;而与之相关的问题&#xff0c;如抖音本地生活城市代理保证金等更是长居相关社群话题榜的前列。 而就目前的市场情况来…

2024华为数通HCIP-datacom最新题库(H12-831变题更新⑧)

请注意&#xff0c;华为HCIP-Datacom考试831已变题 请注意&#xff0c;华为HCIP-Datacom考试831已变题 请注意&#xff0c;华为HCIP-Datacom考试831已变题 近期打算考HCIP的朋友注意了&#xff0c;如果你准备去考试&#xff0c;还是用的之前的题库&#xff0c;切记暂缓。 1、…

抖音开放平台API接口如何开发||抖音相关接口数据采集数据分析 【附实例】

抖音开放平台提供了多种接口&#xff0c;包括授权登录、用户信息、视频管理、评论互动、消息通知、数据分析等。 以下是开发抖音接口的一些步骤&#xff1a; 1. 注册开发者账号&#xff1a;在抖音开放平台上注册开发者账号&#xff0c;获取开发者身份认证。 2. 创建应用&…

学习鸿蒙-利用Code Linter检查代码

如何检查&#xff1a; 1.在已打开的代码编辑器窗口单击右键点击Code Linter&#xff0c;或在工程管理窗口中鼠标选中单个或多个工程文件/目录&#xff0c;右键选择Code Linter > Full Linter执行代码全量检查。 2.通过git 提交&#xff0c;可在commit界面右下角点击齿轮图标…

嵌入式开发问题总汇

1. 事情经过&#xff1a;有天快下班的时候&#xff0c;我们采购经理找到我&#xff0c;说&#xff0c;生成的bin文件通过软件上传烧录时&#xff0c;烧录就会重启&#xff0c;但是其他的板子就不会。最后他换了一个A厂家的flash芯片&#xff0c;就可以了。但是&#xff0c;B厂…

使用 Postman 进行 Trello API 自动化测试的完整指南

文章目录 前言一、自动化测试是什么&#xff1f;二、比较自动化测试与手工测试1. 自动化测试2. 手工测试 三、环境搭建1.创建Collection2.创建环境变量3.添加API请求 四、设计测试用例1. API简单调用2. 获取所有emoji3. 创建一个新看板&#xff1a;4. 获得创建的看板信息5. 在看…

【Git】git 从入门到实战系列(一)—— Git 的诞生,Linus 如何在 14 天内编写出 Git?

<> 博客简介&#xff1a;Linux、rtos系统&#xff0c;arm、stm32等芯片&#xff0c;嵌入式高级工程师、面试官、架构师&#xff0c;日常技术干货、个人总结、职场经验分享   <> 公众号&#xff1a;嵌入式技术部落   <> 系列专栏&#xff1a;C/C、Linux、rt…

【机器学习】决策边界的基本概念以及如何在逻辑回归中找到决策边界

引言 在机器学习中&#xff0c;决策边界是分类算法用来区分不同类别数据点的线、面或超平面。这些边界通常是模型的预测规则&#xff0c;用于将特征空间中的点分配到不同的类别。决策边界可以是线性的&#xff0c;也可以是非线性的&#xff0c;取决于数据的分布和所使用的分类算…