单调栈(C/C++)

news2025/1/8 12:17:16

目录

1. 单调栈的定义

2. 单调栈的常见用途

3. 案例分析

3.1 暴力解法

 3.2 单调栈

 4. 单调栈总结


1. 单调栈的定义

单调栈顾名思义,就是栈内的元素是单调的。根据栈内元素的单调性的不同,可以分为:

单调递增栈:栈内元素是单调递增的栈。

单调递减栈:栈内元素是单调递减的栈。

2. 单调栈的常见用途

单调栈的用途:给定一个序列,指定一个序列中的元素,求解该元素 左侧/右侧 第一个比自身    小/大的元素。

这便是单调栈的常见用途。下面结合具体的例子来理解单调栈哈!N

3. 案例分析

原题链接:

496. 下一个更大元素 I - 力扣(LeetCode)icon-default.png?t=N176https://leetcode.cn/problems/next-greater-element-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] 是如上所述的 下一个更大元素 。

3.1 暴力解法

暴力解法的思路就比较简单了。我们先初始化一个数组 ret,用他的下标表示 nums2 中的每一个元素,对应下标的值表示右侧第一个比它大的元素。然后从后往前(从前向后也行的)遍历 nums2 数组中的元素,遍历每一个元素时向后找比该元素更大的数,如果找到则将对应的结果保存到 下标为遍历元素的位置处,如果没有找到的话就将 -1 保存到下标为遍历元素的位置处。

得到了 nums2 数组中每个元素的右边第一个比自身大的元素后,只需要遍历一次 nums1 数组,在 ret 数组中找到结果就行啦!!

假设 nums2 数组的大小为 N,在求 nums2 数组中的每一个元素右侧第一个比自身大的数时,时间复杂度是一个等差数列的求和,即 O(N*N) 。在遍历 nums1 数组时,因为 nums1 数组中的元素是nums2 数组中元素的子集,遍历 nums1 的时间复杂度为 O(N),所以总的时间复杂度为:O(N^2)。

int* nextGreaterElement(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize)
{
    int ret[10010];
    int j;
    for(int i = nums2Size - 1; i>=0;i--)
    {
        for(j =  i + 1; j < nums2Size; j++)
        {
            if(nums2[j] > nums2[i])
            {
                ret[nums2[i]] = nums2[j];
                break;
            }
        }
        if(j == nums2Size)
        {
            ret[nums2[i]] = -1;
        }
    }
    *returnSize = nums1Size;
    int* array = (int*)malloc(sizeof(int)*nums1Size);
    for(int i = 0;i<nums1Size;i++)
    {
        array[i] = ret[nums1[i]];
    }
    return array;
}

 3.2 单调栈

单调栈的应用思路和双指针算法大体思路是一致的。先分析暴力解法怎么做,然后分析具体题目,找到其中隐藏的性质,从而达到优化时间效率的目的。

emm怎么分析的就不说了,后面会总结的。经过分析该问题发现:在向右找比自身大的元素时,哪些下标更大的,但是值却更小的元素是不可能作为结果输出到 ret 数组的。

 分析到这里我们就可以用单调栈(为啥呢?找的是右侧第一个比自身大的元素,第一这两个字很能说明问题)来解决问题了!!我们这里使用的是用数组模拟的栈哈!效率比STL更高一点。向右找比自身大元素时,需要从后往前遍历 nums2 数组。

我们还是以上面的 3 4 7 2 5 来举例分析,开始遍历到 5 这个元素,此时栈为空,那就表明 5 这个元素右侧没有比自身大的元素(这里也能够说明为啥向右找需要从后往前遍历),将结果保存到 ret 数组。然后将 5 压入栈中。遍历到 2 时发现 2 小于栈顶的元素 5,表明 2 这个元素右侧第一个比自身大的元素是 5,将结果保存到 ret 数组中。遍历到 7 时,发现 7 大于栈顶的元素 2,这就是我们刚才说的,2 是不可能作为结果输出的,所以需要将栈顶的 2 弹出。弹出之后栈顶的元素就是 5 啦,同样 小于 7,但下标大于 7 的下标,需要再次弹出。此时我们发现栈里面已经没有元素了,说明 7 的右侧没有比自身大的元素 返回 -1。然后将 7 压入栈中。其他的元素就同理啦!

 

同样假设 nums2 数组的大小为 N,我们经过分析不难发现,nums2 中的元素 最多被 压栈一次,弹栈一次,所以找 nums2 数组中 右侧第一个 比自身大的数的时间复杂度为 O(N),遍历nums1数组输出结果时也是 O(N),因此总的时间复杂度就是 O(N)。

int* nextGreaterElement(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize){
    int stack[nums2Size + 1], top = 0;
    int ret[10010];
    for(int i = nums2Size - 1; i >= 0; i--)
    {
        while(top && nums2[i] >= stack[top])
        {
            top--;
        }
        if(top)
        {
            ret[nums2[i]] = stack[top];
        }
        else
        {
            ret[nums2[i]] = -1;
        }
        stack[++top] = nums2[i];
    }

    int* array = (int*)malloc(sizeof(int)*nums1Size);
    for(int i = 0;i<nums1Size;i++)
    {
        array[i] = ret[nums1[i]];
    }
    *returnSize = nums1Size;
    return array;
}

 4. 单调栈总结

单调栈的常见用途就是这个啦!显然是有四种情况的:

1:向左找第一个比自身大的数。

2:向左找第一个比自身小的数。

3:向右找第一个比自身大的数。

4:向右找第一个比自身小的数。

通过以上的解题我们可能会有以下问题:

1:从前往后遍历还是从后往前遍历?

答:向右找数从后往前遍历,向左找数从前往后遍历。

2:弹栈的条件之一是大于等于还是小于等于?

答:找比自身大的数是大于等于正在遍历的数,找比自身小的数是小于等于正在遍历的数。

 

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

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

相关文章

LeetCode 105. 从前序与中序遍历序列构造二叉树 106. 从中序与后序遍历序列构造二叉树

为什么前序和中序或者中序和后序&#xff0c;两两组合能构建一个二叉树&#xff1f; 因为前序和后序可以确定根&#xff0c;而中序可以划分出左右区间。 文章目录从前序与中序遍历序列构造二叉树从中序与后序遍历序列构造二叉树从前序与中序遍历序列构造二叉树 难度 中等 题目链…

基于java的进销库存管理系统(Vue+Springboot+Mysql)前后端分离项目,附万字课设论文

1.3 系统实现的功能 本次设计任务是要设计一个超市进销存系统&#xff0c;通过这个系统能够满足超市进销存系统的管理及员工的超市进销存管理功能。系统的主要功能包括&#xff1a;首页、个人中心、员工管理、客户管理、供应商管理、承运商管理、仓库信息管理、商品类别管理、 …

TS泛型,原来就这?

一、泛型是什么&#xff1f;有什么作用&#xff1f; 当我们定义一个变量不确定类型的时候有两种解决方式&#xff1a; 使用any 使用any定义时存在的问题&#xff1a;虽然知道传入值的类型但是无法获取函数返回值的类型&#xff1b;另外也失去了ts类型保护的优势 使用泛型 泛型…

记一次线上es慢查询导致的服务不可用

现象 某日线上业务同学反馈订单列表查询页面一直loding&#xff0c;然后提示请求超时&#xff0c;几分钟之后恢复正常 接到报障之后&#xff0c;马上根据接口URL&#xff0c;定位到了请求链路&#xff0c;发现是es查询超时&#xff0c;这里我们的业务订单表数据是由几百万的&a…

【数据结构】时间复杂度和空间复杂度以及相关OJ题的详解分析

​ ​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;数据结构 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录1.算法效率1.1 如何衡…

独家 | Gen-1——可以改变视频风格的AI模型

翻译&#xff1a;吴振东校对&#xff1a;张睿毅本文约1000字&#xff0c;建议阅读3分钟 本文简单介绍了Runway公司的发展史&#xff0c;以及他们新推出的生成式AI模型Gen-1&#xff0c;可用于通过应用文本提示或者参考图像所指定的任意风格&#xff0c;将现有视频转换为新视频。…

php mysql高校教材管理系统

我的目标就是在于开发一个功能实用、操作方便&#xff0c;简单明了的管理系统&#xff1b;其能够录入教师个人的信息&#xff0c;教导主任信息&#xff0c;在操作上能够完成诸如添加、修改、删除、按各种条件进行查询、等方面的工作&#xff0c;基本满足学校的日常业务的需求. …

System V|共享内存基本通信框架搭建|【超详细的代码解释和注释】

前言 那么这里博主先安利一下一些干货满满的专栏啦&#xff01; 手撕数据结构https://blog.csdn.net/yu_cblog/category_11490888.html?spm1001.2014.3001.5482这里包含了博主很多的数据结构学习上的总结&#xff0c;每一篇都是超级用心编写的&#xff0c;有兴趣的伙伴们都支…

string和自动推断类型

欢迎来观看温柔了岁月.c的博客目前设有C学习专栏C语言项目专栏数据结构与算法专栏目前主要更新C学习专栏&#xff0c;C语言项目专栏不定时更新待C专栏完毕&#xff0c;会陆续更新C项目专栏和数据结构与算法专栏一周主要三更&#xff0c;星期三&#xff0c;星期五&#xff0c;星…

【项目管理】项目进度管理中的逻辑关系

项目的进度管理是项目核心管理之一&#xff0c;通过合理的进度安排&#xff0c;制定出科学可行的分项工期表&#xff0c;并条理清晰的显示出项目进度之间的逻辑关系。 1、目标是计划的灵魂 进度计划必须按照确定的项目总进度要求进行编制&#xff0c;了解项目总目标和整体安…

网络安全——数据链路层安全协议(2)

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 一.局域网数据链路层安全协议 1.IEEE 802.10 &#xff08;1&#xff09;IEE…

JavaWeb HTTP和Maven

4、Http 4.1、什么是HTTP HTTP&#xff08;超文本传输协议&#xff09;是一个简单的请求-响应协议&#xff0c;它通常运行在TCP之上。 文本&#xff1a;html&#xff0c;字符串&#xff0c;~ ….超文本&#xff1a;图片&#xff0c;音乐&#xff0c;视频&#xff0c;定位&am…

登峰造极,师出造化,Pytorch人工智能AI图像增强框架ControlNet绘画实践,基于Python3.10

人工智能太疯狂&#xff0c;传统劳动力和内容创作平台被AI枪毙&#xff0c;弃尸尘埃。并非空穴来风&#xff0c;也不是危言耸听&#xff0c;人工智能AI图像增强框架ControlNet正在疯狂地改写绘画艺术的发展进程&#xff0c;你问我绘画行业未来的样子&#xff1f;我只好指着Cont…

jdbc模板的基本使用

1.JdbcTemplate的开发步骤 <1>导入spring-jdbc和spring-tx坐标 <2>创建数据库表和实体 <3>创建JdbcTemplate对象 <4>执行数据库 2.JdbcTemplate快速入门 <1>导入坐标 <dependency><groupId>org.springframework</groupId><…

【Python学习笔记】第十七节 Python 异常处理

Python 异常在任何一种编程语言中&#xff0c;都会有异常处理机制&#xff0c;python也不例外&#xff0c;它提供了两个非常重要的功能来处理python程序在运行中出现的异常和错误。Python 异常处理异常的概念&#xff1a;在程序运行过程中&#xff0c;由于代码错误或者运行环境…

Java数据结构与算法——手撕LRULFU算法

LRU算法 力扣146&#xff1a;https://leetcode-cn.com/problems/lru-cache/ 讲解视频&#xff1a;https://www.bilibili.com/video/BV1Hy4y1B78T?p65&vd_source6f347f8ae76e7f507cf6d661537966e8 LRU是Least Recently Used的缩写&#xff0c;是一种常用的页面置换算法&…

Typora图床配置:Typora + PicGo + 阿里云OSS

文章目录一、前景提要二、相关链接三、搭建步骤1. 购买阿里云对象存储OSS2. 对象存储OSS&#xff1a;创建Bucket3. 阿里云&#xff1a;添加OSS访问用户及权限4. 安装Typora5. 配置PicGo方法一&#xff1a;使用PicGo-Core (Command line)方法二&#xff1a;使用PicGo(app)6. 最后…

C语言深度剖析:关键字

C语言深度剖析:关键字C语言深度剖析:关键字前言定义与声明&#xff08;补充内容&#xff09;最宏大的关键字-auto最快的关键字-register关键字static被冤枉的关键字-sizeof整型在内存中的存储原码、反码、补码大小端补充理解变量内容的存储和取出为什么都是补码整型取值范围关于…

多线程的初识和创建

✨个人主页&#xff1a;bit me&#x1f447; ✨当前专栏&#xff1a;Java EE初阶&#x1f447; ✨每日一语&#xff1a;知不足而奋进&#xff0c;望远山而前行。 目 录&#x1f4a4;一. 认识线程&#xff08;Thread&#xff09;&#x1f34e;1. 线程的引入&#x1f34f;2. 线程…

【计算机网络:自顶向下方法】Chapter4 网络层:数据平面

Chapter44.1 网络层概述4.1.1 网络层服务4.1.2 网络层的主要功能转发&#xff08;局部&#xff09;路由选择&#xff08;全局&#xff09;4.1.3 控制平面和数据平面传统方式SDN方式4.1.4 网络服务模型4.2 路由器组成4.2.1 路由器结构概况4.2.2 转发方式4.2.3 输入端口处理与基于…