【数据结构】单调栈、单调队列

news2025/1/12 16:06:05

单调栈

单调栈 = 单调 + 栈

模拟单调递增栈的操作:

  • 如果栈为 空 或者栈顶元素 大于 入栈元素,则入栈;
  • 否则,入栈则会破坏栈内元素的单调性,则需要将不满足条
    件的栈顶元素全部弹出后,将入栈元素入栈。

单调递增栈的伪代码:
在这里插入图片描述
单调栈的作用:

  • 线性的时间复杂度
  • 单调递增栈 可以找到数组中往左/往右第一个比当前元素严格小 < < <(或者不小于 ≥ \geq )的元素
  • 压栈的角度理解,从左至右压入单调栈,将元素 x x x压栈时,此时栈顶元素 t o p top top是往左第一个比 x x x小的元素,因为不存在元素 y y y t o p top top右侧且比 x x x小, y y y一定会成为新的栈顶,这和 t o p top top是栈顶相矛盾
  • 也可以从弹栈的角度理解,元素 x x x第一个弹出元素 y y y x x x往左第一个不小于它的元素,反过来,元素 x x x y y y往右第一个小于它的元素
  • 可能有些元素不存在以上关系
  • 单调递减栈 可以找到数组中往左/往右第一个比当前元素严格大 > > >(或者不大于 ≤ \leq )的元素
  • 可以求得以当前元素为最值的最大区间

代码模板

  • 最大矩形问题:给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。求在该柱状图中,能够勾勒出来的矩形的最大面积。
  • 数组R,L的含义是往左/右最后一个不小于当前元素的元素索引,这个可以通过找到左右第一个小于当前元素的元素,索引 ± 1 \pm1 ±1得到
  • 弹栈时,栈顶元素h[stk.top()]往右第一个小于它的元素是h[i];压栈时,h[i]往左第一个小于它的元素是h[stk.top()]
  • 压栈时,栈空代表左侧没有元素小于h[i],因此L[i]=0;元素最后仍然在栈中,R[stk.top]=n-1
int R[N], L[N];
int largestRectangleArea(vector<int>& h) {
    int n = h.size();
    stack<int> stk;

    for (int i = 0; i < n; i++) {
        while (!stk.empty() && h[stk.top()] >= h[i]) {
            R[stk.top()] = i - 1;
            stk.pop();
        }
        if (!stk.empty()) L[i] = stk.top() + 1;
        else L[i] = 0;
        stk.push(i);
    }
    while (!stk.empty()) {
        R[stk.top()] = n - 1;
        stk.pop();
    }

    int ans  = 0;
    for (int i = 0; i < n; i++) {
        ans = max(ans, h[i] * (R[i] - L[i] + 1));
    }
    return ans;
}
  • 接雨水问题:给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
  • 维护一个单调递减栈,从左到右遍历至下标 i i i,若栈中至少有两个元素,记栈顶元素索引为 t o p top top,栈顶的下一个元素为 l l l,首先根据单调递减性 h [ l ] > h [ t o p ] h[l]>h[top] h[l]>h[top],若 h [ i ] > h [ t o p ] h[i]>h[top] h[i]>h[top],则可以形成宽为 i − l − 1 i-l-1 il1,高为 m i n ( h [ i ] , h [ l ] ) − h [ t o p ] min(h[i],h[l])-h[top] min(h[i],h[l])h[top]的积水区域
  • 实际上 h [ i ] > h [ t o p ] h[i]>h[top] h[i]>h[top]产生弹栈操作
  • 也可以动态规划解法,对于下标 i i i,下雨后水能到达的最大高度等于下标 i i i两边的最大高度的最小值,下标 i i i处能接的雨水量等于下标 i i i处的水能到达的最大高度减 h [ i ] h[i] h[i]
  • 单调栈方法是水平地划分积水区域,动态规划是竖直地划分区域
int trap(vector<int>& h) {
    int ans = 0;
    int n = h.size();
    stack<int> stk;

    for (int i = 0; i < n; i++) {
        while (!stk.empty() && h[stk.top()] <= h[i]) {
            int itop = stk.top(); stk.pop();
            if (stk.size() > 0) {
                ans += (min(h[stk.top()], h[i]) - h[itop]) * (i - stk.top() - 1);
            }
        }
        stk.push(i);
    }
    return ans;
}

单调队列

单调队列 = 单调 + 队列

在这里插入图片描述

  • 单调队列的维护过程与单调栈相似
  • 区别在于
  • 单调栈只维护一端(栈顶), 而单调队列可以维护两端(队首和队尾)
  • 单调栈通常维护 全局 的单调性, 而单调队列通常维护 局部 的单调性
  • 由于单调队列 可以队首出队 以及 前面的元素一定比后面的元素先入队 的
    性质,使得它可以维护局部的单调性
  • 考虑区间 [ L , R ] [L,R] [L,R],依次将区间内的数加入单调递增队列,则队首是区间最小
    值。若区间要变成 [ L + 1 , R + 1 ] [L+1,R+1] [L+1,R+1],将 R + 1 R+1 R+1加入队列。判断第 L L L个元素是不是队首。如果不是队首,说明队首元素在 L L L后面,是区间 [ L + 1 , R + 1 ] [L+1,R+1] [L+1,R+1]最小值。如果是队首,将队首出队,新队首是区间 [ L + 1 , R + 1 ] [L+1,R+1] [L+1,R+1]最小值。
  • 时间复杂度与单调栈一致

代码模板

  • 滑动窗口最大值问题:给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。返回滑动窗口中的最大值 。
  • 可能存在同时求最大最小值的情况,为了提高代码复用性,将单调队列封装为find()方法
  • 单调递减栈可以维护区间最大值,我们将序列元素全部取反,又可以维护最小值,记录答案时通过参数opt还原序列
vector<int> find(int opt, vector<int>& a, int k) {
   deque<int> Q;
   int n = a.size();
   vector<int> ans;
   for (int i = 0; i < n; i++) {
       while (!Q.empty() && Q.front() < i - k + 1) Q.pop_front();
       while (!Q.empty() && a[Q.back()] < a[i]) Q.pop_back();
       Q.push_back(i);
       if (i >= k - 1) {
           ans.push_back(opt * a[Q.front()]);
       }
   }
   return ans;
}
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
   return find(1, nums, k);
}

more

之前补的一道单调队列优化dp,b题
https://blog.csdn.net/qq_23096319/article/details/119935127
一个网格图,一些格子可以着陆,一些不可以,一次可以至多跳 k k k步,问从最左上角跳到最右下角的最少步数。
重新整理一下思路:定义状态f[i][j]代表从最左上角跳到该格子的最少步数,有状态转换方程f[i][j]=min(f[x][y]),其中(i-x<=k && j==y) || (j-y<=k && i==x) && ok[x][y] && ok[i][j],由于是按行扫描,所以只需要维护一个行队列,到下一行清空,每一列均需维护一个单调队列。

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

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

相关文章

研究分析如何设计高并发下的弹幕系统

一、需求背景为了更好的支持直播业务&#xff0c;产品设计为直播业务增加弹幕功能,但是最初的弹幕设计使用效果并不理想&#xff0c;经常出现卡顿、弹幕偏少等需要解决的问题。二、问题分析按照背景来分析&#xff0c;系统主要面临以下问题&#xff1a;带宽压力&#xff1b;弱网…

[基础]qml基础控件

TextText元素可以显示纯文本或者富文本(使用HTML标记修饰的文本)。它有font,text,color,elide,textFormat,wrapMode,horizontalAlignment,verticalAlignment等属性。主要看下clip&#xff0c;elide&#xff0c;textFormat&#xff0c;warpMode属性clipText 项目是可以设置宽度的…

Apache Spark 机器学习 特征抽取 4-2

Word2Vec 单词向量化是一个估算器&#xff0c;将文档转换成一个按照固定顺序排列的单词序列&#xff0c;然后&#xff0c;训练成一个Word2VecModel单词向量化的模型&#xff0c;该模型将每个单词映射成一个唯一性的、固定大小的向量集&#xff0c;对每个文档的所有单词进行平均…

【数据结构和算法】实现线性表中的静态、动态顺序表

本文是数据结构的开篇&#xff0c;上文学习了关于使用C语言实现静态、动态、文件操作的通讯录&#xff0c;其中使用到了结构体这一类型&#xff0c;实际上&#xff0c;是可以属于数据结构的内容&#xff0c;接下来我们来了解一下顺序表的相关内容。 目录 前言 一、线性表 一…

流批一体计算引擎-6-[Flink]的Python DataStream API程序

参考官方Python API文档 1 IDEA中运行Flink 从Flink 1.11版本开始, PyFlink 作业支持在 Windows 系统上运行&#xff0c;因此您也可以在 Windows 上开发和调试 PyFlink 作业了。 1.1 环境配置 pip3 install apache-flink1.15.3 CMD>set PATH查看环境变量 CMD>set JAV…

对JDBC驱动注册--DriverManager.registerDriver和Class.forName(driverClass)的理解

对JDBC驱动注册–DriverManager.registerDriver和Class.forName(driverClass)的理解 JDBC提供了独立于数据库的统一API&#xff0c;MySQL、Oracle等数据库公司都可以基于这个标准接口来进行开发。包括java.sql包下的Driver&#xff0c;Connection&#xff0c;Statement&#x…

注解方式管理Bean

1.注解方式创建对象IOC 导入依赖 aop Component(父注解) 放在类上,用于标记,告诉spring当前类需要由容器实例化bean并放入容器中 该注解有三个子注解 Controller 用于实例化controller层bean Service 用于实例化service层bean Repository 用于实例化持久层bean 当不确定是哪一…

【刷题大本营】二叉树进阶oj题(动图讲解,附代码及题目链接)

&#x1f525;&#x1f525; 欢迎来到小林的博客&#xff01;&#xff01;       &#x1f6f0;️博客主页&#xff1a;✈️小林爱敲代码       &#x1f6f0;️欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言       这篇文章给大家带来一…

RK3399平台开发系列讲解(文件系统篇)文件回写过程介绍

🚀返回专栏总目录 文章目录 一、编程接口二、回写过程2.1、周期回写2.2、强制回写2.3、系统调用sync沉淀、分享、成长,让自己和他人都能有所收获!😄 📢进程写文件时,内核的文件系统模块把数据写到文件的页缓存,没有立即写回到存储设备。文件系统模块会定期把脏页(即…

[JavaEE]线程池

专栏简介: JavaEE从入门到进阶 题目来源: leetcode,牛客,剑指offer. 创作目标: 记录学习JavaEE学习历程 希望在提升自己的同时,帮助他人,,与大家一起共同进步,互相成长. 学历代表过去,能力代表现在,学习能力代表未来! 目录: 1. 线程池是什么? 2. 线程池的实现原理 3. 标准…

Eureka集群构建步骤

目录 一、Eureka集群原理说明 二、EurekaServer集群环境构建步骤 三、将支付服务8001微服务发布到上面2台Eureka集群配置中 四、将订单服务80微服务发布到上面2台Eureka集群配置中 五、测试01 六、支付服务提供者8001集群环境构建 七、负载均衡 八、测试02 一、Eureka集…

论文投稿指南——中文核心期刊推荐(建筑科学)

【前言】 &#x1f680; 想发论文怎么办&#xff1f;手把手教你论文如何投稿&#xff01;那么&#xff0c;首先要搞懂投稿目标——论文期刊 &#x1f384; 在期刊论文的分布中&#xff0c;存在一种普遍现象&#xff1a;即对于某一特定的学科或专业来说&#xff0c;少数期刊所含…

前同事居然因为 Pycharm 的这个功能,即使离职三年也依然经常被请去喝茶~

大家好&#xff0c;我是 哈士奇 &#xff0c;一位工作了十年的"技术混子"&#xff0c; 致力于为开发者赋能的UP主, 目前正在运营着 TFS_CLUB社区。 &#x1f4ac; 人生格言&#xff1a;优于别人,并不高贵,真正的高贵应该是优于过去的自己。&#x1f4ac; &#x1f4e…

教你一键生成形如Springboot的高大上banner打印效果

背景 今天闲来无聊&#xff0c;想搞一个类似于Springboot项目启动时打印效果&#xff0c;如下图&#xff1a; 问题解决方案 那这个打印效果怎么实现的呢&#xff1f; 其实&#xff0c;对于这个中效果实现起来也是很简单的。毕竟依托于Springboot强大的框架&#xff0c;任何问…

网狐大联盟非联盟成员无法创建房间解决-暂时不可创建当前游戏,请选择其他游戏!

"暂时不可创建当前游戏,请选择其他游戏!" 问题所有lua文件定位:

恶意代码分析实战 16 Shellcode分析

16.1 Lab19-01 将程序载入IDA。 一堆ecx自增的操作。到200是正常的代码段。 shellcode的解码器也是从这里开始的&#xff0c;一开始的xor用于清空ecx&#xff0c;之后将18dh赋给cx&#xff0c;jmp来到loc_21f,而在下图可以看到loc_21调用sub_208,在call指令执行后&#xff0…

40.Isaac教程--3D 物体姿态优化

3D 物体姿态优化 ISAAC教程合集地址: https://blog.csdn.net/kunhe0512/category_12163211.html 3D 物体姿态优化在操作等应用中起着至关重要的作用&#xff0c;在这些应用中&#xff0c;检测到的物体的位置会影响机器人的整体性能。 Isaac SDK 中的 3D 对象姿势优化应用程序提…

7. 好客租房-项目日常推进ing

7. 好客租房-项目日常推进ing 本章节不涉及大量内容,主要是为了推荐项目代码日常进度而设置, 包括添加mock接口, 添加更新房源接口, 为系统添加缓存. 7.1 为前端系统提供mock服务 往往在项目开发中, 为实现前后端并行开发&#xff0c;后端需要对前端所有的请求都都进行支持。…

2022年度总结——2022我在CSDN的那些事暨2023我的目标展望:Pursue freedom Realize self-worth

&#x1f4cb;前言 关于年度征文&#xff1a; 活动地址&#xff1a;2022年度征文活动页-CSDN &#x1f4da;文章目录 &#x1f4cb;前言 &#x1f3af;再见2022&#xff0c;2023新年快乐 &#x1f3af;回顾2022——“我”与我在CSDN的那些事 &#x1f9e9;伊始——CSDN&…

Allegro如何做镂空丝印操作指导

Allegro如何做镂空丝印操作指导 在PCB设计丝印的时候,会需要画镂空的丝印,Allegro升级到了172版本的时候,可以画镂空的丝印,如下图 具体操作如下 选择Shape Add Rect命令Options选择需要画到的层面,比如Silkscreen TOP层