吃透单调栈(1)——单调栈入门

这篇具有很好参考价值的文章主要介绍了吃透单调栈(1)——单调栈入门。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

单调栈是一种理解起来很容易,但是运用起来并不那么简单的数据结构。一句话解释单调栈,就是一个栈,里面的元素的大小按照他们所在栈内的位置,满足一定的单调性。

 

单调栈摸版

下面维护一个顶大底小的的单调栈(单调递减栈)

stack<int> st;
for(int i = 0; i < nums.size(); i++)
{
    while(!st.empty() && st.top() > nums[i])
    {
        st.pop();
    }
    st.push(nums[i]);
}

 

开胃小菜

题目是这样的,给一个数组,返回一个大小相同的数组。返回的数组的第i个位置的值应当是,对于原数组中的第i个元素,至少往右走多少步,才能遇到一个比自己大的元素(如果之后没有比自己大的元素,或者已经是最后一个元素,则在返回数组的对应位置放上-1)。

简单的例子:

input: 5,3,1,2,4

return: -1 3 1 1 -1

explaination: 对于第0个数字5,之后没有比它更大的数字,因此是-1,对于第1个数字3,需要走3步才能达到4(第一个比3大的元素),对于第2和第3个数字,都只需要走1步,就可以遇到比自己大的元素。对于最后一个数字4,因为之后没有更多的元素,所以是-1。

暴力做的结果就是O(n^2)的时间复杂度,例如对于一个单调递减的数组,每次都要走到数组的末尾。那么用单调栈怎么做呢?先来看代码:

vector<int> nextExceed(vector<int> &input) {
    vector<int> result (input.size(), -1);
    stack<int> monoStack;
    for(int i = 0; i < input.size(); ++i) {    
        while(!monoStack.empty() && input[monoStack.top()] < input[i]) {
            result[monoStack.top()] = i - monoStack.top();
            monoStack.pop();
        }
        monoStack.push(i);
    }
    return result;
}

我们维护这样一个单调递减的stack,stack内部存的是原数组的每个index。每当我们遇到一个比当前栈顶所对应的数(就是input[monoStack.top()])大的数的时候,我们就遇到了一个“大数“。这个”大数“比它之前多少个数大我们不知道,但是至少比当前栈顶所对应的数大。我们弹出栈内所有对应数比这个数小的栈内元素,并更新它们在返回数组中对应位置的值。因为这个栈本身的单调性,当我们栈顶元素所对应的数比这个元素大的时候,我们可以保证,栈内所有元素都比这个元素大。对于每一个元素,当它出栈的时候,说明它遇到了自己的next greater element,我们也就要更新return数组中的对应位置的值。如果一个元素一直不曾出栈,那么说明不存在next greater element,我们也就不用更新return数组了。

 

举一反三

下面用相同的套路做一道Leetcode题:1475. 商品折扣后的最终价格

 vector<int> finalPrices(vector<int> &input)
    {
        // 单调栈实现 O(1)
        vector<int> result = input;
        stack<int> monoStack;
        for (int i = 0; i < input.size(); ++i) {
            while (!monoStack.empty() && input[monoStack.top()] >= input[i]) {
                result[monoStack.top()] = input[monoStack.top()] - input[i];  // 更新
                monoStack.pop();  // 出栈  (注意,每个元素仅出栈一次)
            }
            monoStack.push(i);
        }
        return result;
    }

 文章来源地址https://www.toymoban.com/news/detail-691438.html

 

到了这里,关于吃透单调栈(1)——单调栈入门的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Facebook HiPlot “让理解高维数据变得容易”

    在这个全球信息化的时代,数据量呈爆炸式增长,数据的复杂性也是如此。如何有效地处理高维数据并找到隐藏在其中的相关性和模式是一个严峻的挑战。近年来,可视化和可视化分析已被应用于该任务,并取得了一些积极成果。Facebook的新HiPlot是一个轻量级的交互式可视化

    2024年02月11日
    浏览(31)
  • 为什么分类问题不能使用mse损失函数,更容易理解版本

    分类问题通常不适合使用均方误差(Mean Squared Error,MSE)损失函数,原因如下: 输出差异的度量不同:MSE损失函数是基于预测值和真实值之间的差异的平方和进行计算的,适用于回归问题(建立一个模型来预测连续数值输出的问题, eg: 房价预测;股票价格预测…),其中

    2024年04月26日
    浏览(26)
  • 第六章、用户体验五要素之框架层解析(本文作用是通俗讲解,让你更容易理解)

            结构层定义产品运行形式,框架层则用于确定用什么样的功能或者形式来实现。在框架层,功能型和信息型产品都需要信息设计,不同的是功能型还需要界面设计,而信息型产品则是导航设计。         1、界面设计:如果涉及提供给用户做某些事的能力,那就是界

    2024年02月09日
    浏览(32)
  • 从零开始实现C++ TinyWebServer(六)---- 这或许是你见过的最容易理解的HTTP连接

    今天上完体育课打完球发现了一家咖啡店,我之前一直纳闷数据谷里面没有咖啡店呢,结果今天就给我找到了。这家咖啡店的位置开的非常隐蔽,一到门口一条小狗就一直贴着我闻,走到店里面去点咖啡,店里装修的还不错,在这个位置也挺安静的,店里的咖啡师小姐姐说好

    2024年02月11日
    浏览(36)
  • 一文带你入门并吃透状态压缩DP

    【本文比较适合有一定动态规划和位运算基础的童鞋阅读】 首先先讲讲什么是状态压缩 状态压缩 就是使用某种方法,简明扼要地以最小代价来表示某种状态,通常是用一串01数字(二进制数)来表示各个点的状态。这就要求使用状态压缩的对象的点的状态必须只有两种,0

    2024年02月09日
    浏览(35)
  • FPGA入门有多难?这篇文章让你吃透零基础入门技巧!

    FPGA是一个高度集成化的芯片,其学习过程既需要编程,又需要弄懂硬件电路和计算机架构。涉及到的知识和基础非常多, 如果不合理地安排学习内容,学习过程会非常漫长和枯燥 。这使很多想要学习FPGA小伙伴望而却步,那么,**FPGA到底有多难入门?**今天移知教育小编就带

    2024年02月04日
    浏览(39)
  • 一种可理解的线性transformer

    找到一种更简洁的形式,如下: message passing公式。其含义是,每个token产生一个消息 Q i Q_i Q i ​ 。然后消息通过权重 m i m_i m i ​ 加权合并,最后通过权重 s i s_i s i ​ 消息分发。极限情况下(即softmax中有一个token的值为1,其余token值为0)每一次传递,会将某个token的消息传给

    2024年02月10日
    浏览(35)
  • 设计模式的另一种有趣理解

    单例模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可使用。 俺有6个漂亮的老婆,她们的老公都是我,我就是我们家里的老公Sigleton,她们只要说道“老公”,都是指的同一

    2024年02月22日
    浏览(36)
  • nginx入门mobi,HTTP规范中的那些容易掉进去的坑

    JAVA异常分类及处理 异常分类 异常的处理方式 Throw和throws的区别 JAVA反射 动态语言 反射机制概念 (运行状态中知道类所有的属性和方法) Java反射API 反射使用步骤(获取Class对象、调用对象方法) 获取Class对象的3种方法 创建对象的两种方法 JAVA注解 JAVA内部类 JAVA泛型 JAVA序

    2024年03月10日
    浏览(35)
  • 【Docker】深入理解Docker:一种革新性的容器技术

    前言   Docker 是一个 开源的应用容器引擎 ,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux或Windows 操作系统的机器上,也可以实现虚拟化,容器是完全使用沙箱机制,相互之间不会有任何接口。 📕作者简介: 热爱跑步的恒川 ,致

    2024年02月05日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包