首页 > 其他分享 >单调栈笔记

单调栈笔记

时间:2024-11-05 22:42:30浏览次数:1  
标签:int tt 元素 栈顶 笔记 stk 单调

单调栈笔记

单调栈,顾名思义,就是把元素按照严格单调的顺序存在栈中(从「栈顶」到「栈底」)

可以加速查找数组中满足特定条件的数的过程,例如:

  • 寻找左侧第一个比当前元素大的元素
  • 寻找左侧第一个比当前元素小的元素
  • 寻找右侧第一个比当前元素大的元素
  • 寻找右侧第一个比当前元素小的元素

可以有效的将嵌套搜索的 \(O(n^2)\) 的时间复杂度优化到 \(O(n)\)

关于栈的实现,可以使用 \(STL\) 中的 stack 库实现,也可以利用数组和栈顶指针进行模拟操作(速度略快)

这里举一道例题具体说明:题目如下


题目描述

给出项数为 \(n\) 的整数数列 \(a_{1 \dots n}\)

定义函数 \(f(i)\) 代表数列中第 \(i\) 个元素之后第一个大于 \(a_i\) 的元素的下标,即 \(f(i)=\min_{i<j\leq n, a_j > a_i} \{j\}\)。若不存在,则 \(f(i)=0\)

试求出 \(f(1\dots n)\)。

输入格式

第一行一个正整数 \(n\)

第二行 \(n\) 个正整数 \(a_{1\dots n}\)

输出格式

一行 \(n\) 个整数表示 \(f(1), f(2), \dots, f(n)\) 的值

样例输入

5
1 4 2 3 5

样例输出

2 5 4 5 0

数据规模与约定

对于 \(30\%\) 的数据,\(n\leq 100\)

对于 \(60\%\) 的数据,\(n\leq 5 \times 10^3\)

对于 \(100\%\) 的数据,\(1 \le n\leq 3\times 10^6\),\(1\leq a_i\leq 10^9\)


首先介绍第一种方法——利用stack库实现:

题目要求查找 \(a_i\) 之后的元素,所以我们需要先将所有数据存进数组,然后反向搭建单调栈

for (int i = 1; i <= n; i++) scanf("%d", &input[i]);//存入数组input

反向操作:

for (int i = n; i > 0; i--) {
	while (!stk.empty() && inp[stk.top()] <= inp[i]) stk.pop();
	ans[i] = stk.empty() ? 0 : stk.top();
	stk.push(i);
}

逐语句解释原理:

while (!stk.empty() && input[stk.top()] <= input[i]) stk.pop();

stk 栈不为空并且栈顶元素所存的下标值使得input[stk.top()] <= input[i]

即当前选择的input[i]的值,大于等于input[stk.top()] 的值时,弹出栈顶元素

stk.push(i);

搭配赋值语句,将下标 i 存进栈中,这样我们就能保证栈中的元素是严格单调递增

在退出 for 遍历前,还需要把查找到的目标元素的下标存进 ans 数组,便于输出答案

ans[i] = stk.empty() ? 0 : stk.top();

这里使用三元运算符,若 stk 栈为空,则把 ans[i] 赋值为 \(0\) ,满足题意

若不为空,则将栈顶元素存储的下标值赋值给 ans[i]

完整代码如下:从下标为 \(1\) 开始,符合题意

int inp[3000010], ans[3000010];
stack<int> stk;

int main()
{
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) scanf("%d", &inp[i]);
	for (int i = n; i > 0; i--) {
		while (!stk.empty() && inp[stk.top()] <= inp[i]) stk.pop();
		ans[i] = stk.empty() ? 0 : stk.top();
		stk.push(i);
	}
	for (int i = 1; i <= n; i++) printf("%d ", ans[i]);
	return 0;
}

接下来介绍模拟栈的实现:

首先定义一个数组空间模拟栈空间,以及一个指针模拟栈顶游标

int stk[3000010],tt;

和利用stack库的操作只有在实现单调栈的循环中有所不同:

for (int i = n; i > 0; i--) {
	while (tt && a[stk[tt]] <= a[i]) tt--;
	ans[i] = tt ? stk[tt] : 0;
	stk[++tt] = i;
}

同样的,首先判断模拟栈空间是否为空,以及当前元素是否大于等于栈顶元素所映射的元素值:

tt && a[stk[tt]] <= a[i]

循环到使栈内元素严格单调递增或空栈时退出

同理,可以写出由栈顶游标 tt 所实现的pop(),top(),empty(),push()操作

我们所定义的栈空间实际上是在stk[0]~stk[tt]的空间内,所以移动栈顶游标的位置,就是对栈顶元素进行操作

完整代码如下:

int a[3000010],stk[3000010],ans[3000010],tt;

int main()
{
	int n;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
	for (int i = n; i > 0; i--) {
		while (tt && a[stk[tt]] <= a[i]) tt--;
		ans[i] = tt ? stk[tt] : 0;
		stk[++tt] = i;
	}
	for (int i = 1; i <= n; i++) printf("%d ", ans[i]);
	return 0;
}

总结:

单调栈就是将无序的元素队列,选出其中的某些元素,构成一个元素值随元素下标的单调变化而单调变化的一个序列

优化实现 \(O(n)\) 的查找时间复杂度

标签:int,tt,元素,栈顶,笔记,stk,单调
From: https://www.cnblogs.com/dianman/p/18529025

相关文章

  • 归龙潮程序逆向笔记 (不定期更新)
    Unity游戏啊,先分析一下文件,Unity2021.3,AB包没加密,Lua看着像异或加密,还有HybridCLR的dll应该是AES之类的看到了libNetHTProtect.so和libmsaoaidsec.so两位老朋友,上frida一把梭!果不其然一开frida就闪退,看闪退的时机大概率在il2cpp前就已经检测了…搜一下msaoaidsec,果然在java层有......
  • JS学习笔记(1)
    目录1.前言2.JavaScript介绍3.JavaScript书写位置4.注释5.输入与输出语法6.变量7.小知识8.总结(其实是我个人的一点扯皮)前言博主的csdn地址:https://blog.csdn.net/2403_87169202今后会两边同时更新,程序员红中,一个努力分享编程干货的全栈开发者,欢迎各位一起讨论学习Ja......
  • 学习笔记(二十五):ArkUi-栅格布局 (GridRow/GridCol)
    概述:栅格布局是一种通用的辅助定位工具,对移动设备的界面设计有较好的借鉴作用。主要优势包括:提供可循的规律:栅格布局可以为布局提供规律性的结构,解决多尺寸多设备的动态布局问题。通过将页面划分为等宽的列数和行数,可以方便地对页面元素进行定位和排版。统一的定位标注:栅格......
  • 学习笔记(二十四):ArkUi-网格 (Grid/GridItem)
    概述:网格布局是由“行”和“列”分割的单元格所组成,通过指定“项目”所在的单元格做出各种各样的布局。网格布局具有较强的页面均分能力,子组件占比控制能力,是一种重要自适应布局,其使用场景有九宫格图片展示、日历、计算器等。ArkUI提供了Grid容器组件和子组件GridItem,用于构建......
  • Mit6.S081笔记:页表笔记
    xv6手册:https://pdos.csail.mit.edu/6.S081/2020/xv6/book-riscv-rev1.pdf相关翻译:http://xv6.dgs.zone/labs/requirements/lab5.html感觉页表很多地方没理解,学习的时候把一些关键地方记录起来,如有错误恳请各位大佬指正。页表笔记​​ 页表是操作系统为每个进程提供私有地址......
  • 2024/11/5日 日志 关于BOM浏览器对象模型和DOM文档对象模型的学习与笔记整理
    和Javascript有关的BOM与DOM及事件监听。以下是今天的内容点击查看代码--BOM--BrowserObjectModel浏览器对象模型--JavaScript将浏览器的各个组成部分封装为对象--组成:--Window:浏览器窗口对象--Navigator:浏览器对象--Screen:屏幕对象--History:历史记录......
  • 操作系统学习笔记-3.1内存管理
    文章目录内存的地址绝对装入静态重定位动态重定位链接覆盖和交换1.覆盖(Overwrite)在内存管理中的作用2.交换(Swap)在内存管理中的作用连续分配管理方式固定分区分配的关键概念优点缺点示例动态分区分配的关键概念优点缺点示例基本分页存储管理基本地址变换机构页表寄存......
  • LDAP--Jenkins详解笔记
    一、Ldap的结构1.组织角色所有用户都可以登录,但是只有创建时的admin组角色有增删改的权限,相当于是根目录,千万不能删,删了就全没了注意,admin用户是首个超级登录用户(相当于根),需要用配置文件生成,详见:https://www.cnblogs.com/wangyuanguang/p/18189832##注意修改wyg部分为自己自......
  • 系统集成项目管理工程师笔记4 - 第四章 信息系统架构
    信息系统集成项目涉及的架构通常有系统架构、数据架构、技术架构、应用架构、网络架构、安全架构;4.1架构基础架构的本质是决策;4.1.1指导思想通过指导思想的贯彻实施,推动项目多元参与者能保持集成关键价值的一致性理解,从而减少不必要的矛盾与冲突;4.1.2设计原则......
  • 黑马程序员JavaWeb开发教程(后端部分---原理篇) ---笔记分享
    目录SpingBoot原理配置优先级Bean管理获取BeanBean作用域第三方BeanSpringBoot原理起步依赖自动配置自动配置原理原理分析要搞清楚SpringBoot的自动配置原理,要从SpringBoot启动类上使用的核心注解@SpringBootApplication开始分析:@SpringBootConfiguration注解上使......