算法总结——单调栈

在这里插入图片描述
纵有疾风起,人生不言弃。本文篇幅较长,如有错误请不吝赐教,感谢支持。

文章目录

    • 一、单调栈的定义
    • 二、单调栈的应用:
      • 寻找左边第一个比它小的数
      • 寻找左边第一个比它小的数的下标
      • 寻找右边第一个小于它的数
      • 寻找右边第一个小于它的数的下标
      • 单调栈总结

一、单调栈的定义

单调栈不是一种新的数据结构,它在结构上仍然是一个普通栈,只是在使用方法上有所区别。单调栈内的元素是单调递增或递减的,因此有单调递增栈和单调递减栈。

  • 单调递增栈: 栈中元素从栈底到栈顶是递增的。
  • 单调递减栈: 栈中元素从栈底到栈顶是递减的。

我们在使用单调栈的时候,要时刻保证栈的单调性,例如,单调递增栈:栈中元素从栈底到栈顶是递增的。当一个元素入栈时,与栈顶比较,若比栈顶大,则入栈;若比栈项小,则弹出栈顶,直到这个数能入栈为止。注意:每个元素都一定入栈
单调栈的核心思想就是:

及时去掉无用数据,保证栈中数据有序

二、单调栈的应用:

单调栈的应用:用来在一个数组中寻找某一个元素左边(或右边)第一个大于(或小于或大于等于或小于等于)它的元素(或元素的下标)。

寻找左边第一个比它小的数

题目练习: AcWing 830. 单调栈
给定一个长度为 N 的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出 −1。
输入格式
第一行包含整数 N,表示数列长度。
第二行包含 N 个整数,表示整数数列。
输出格式
共一行,包含 N 个整数,其中第 i 个数表示第 i 个数的左边第一个比它小的数,如果不存在则输出 −1。
数据范围
1≤N≤10 5 {5} 5
1≤数列中元素≤ 1 0 9 10^{9} 109
输入样例:

5
3 4 2 7 5

输出样例:

-1 3 -1 2 2

思路:传统的暴力做法是双重循环,两个指针i和j,i用来遍历序列,j用来扫描i左边的所有数。时间复杂度是O( n 2 n^{2} n2)

#include<iostream>

using namespace std;

const int Max = 1e5 + 10;

int n,arr[Max];

int main() {
    cin >> n;
    for (int i = 0; i < n; i++) cin >> arr[i];

    for (int i = 0; i < n; i++) {
        bool flag = false;//标志 
        for (int j = i - 1; j>=0; j--) {
            if (arr[j] < arr[i]) {
                flag = true;
                cout << arr[j] << ' ';
                break;
            }
        }
        if (!flag) cout << -1 << ' ';
		//如果flag为false,说明左边没有比该元素小的 
    } 
    return 0;
}

我们要是使用单调栈可以将时间复杂度优化到O(n)。
单调栈的思想( 及时去掉无用数据,保证栈中数据有序):
在指针 i 从左往右遍历的过程中,我们可以用一个栈来保存 i左边的所有元素(不包括i指向的元素),下标越大的元素越接近栈顶,下标越小的元素越接近栈底,然后从栈顶开始找符合条件的(小于该元素),把栈内不符合条件的无用数据全部出栈(大于等于该元素),无用数据之后再也用不到了,最后的栈顶就是答案。
我们来举一个例子:
2 4 5 3 1假如此时3入栈;3左边的4、5都没有存在的必要了(因为3比4和5更小,并且更靠近下一个即将入栈的元素),4、5出栈,然后2比3小,停下符合条件,最后的栈顶2就是答案。
代码:

#include<iostream>
#include<stack>
using namespace std;
const int Max=1e5+10;
int n,arr[Max],ans[Max];
stack<int> stk;
int main()
{
	cin>>n;
	for(int i=0;i<n;i++) cin>>arr[i];//arr完整数组
	for(int i=0;i<n;i++)
	{
		while(!stk.empty()&&stk.top()>=arr[i]) stk.pop();//把栈内不符合条件的无用数据全部出栈(大于等于该元素),
		if(!stk.empty()) ans[i]=stk.top();//最后的栈顶就是答案。
		else ans[i]=-1;
		stk.push(arr[i]);
	}
	for(int i=0;i<n;i++) cout<<ans[i]<<' ';
	return 0;
}

注:arr数组和ans数组可以不创建,但为了方便统一单调栈的格式,所以创建。

寻找左边第一个比它小的数的下标

与上面的差不多,注意到之前我们寻找的是元素所以让栈去保存元素,现在我们寻找下标,所以让栈去保存元素的下标就可以了

#include<iostream>
#include<stack>
using namespace std;
const int Max=1e5+10;
int n,arr[Max],ans[Max];
stack<int> stk;
int main()
{
	cin>>n;
	for(int i=0;i<n;i++) cin>>arr[i];//arr完整数组
	for(int i=0;i<n;i++)
	{
		while(!stk.empty()&&arr[stk.top()]>=arr[i]) stk.pop();//把栈内不符合条件的无用数据全部出栈(大于等于该元素),
		if(!stk.empty()) ans[i]=stk.top();//最后的栈顶就是答案。
		else ans[i]=-1;
		stk.push(i);
	}
	for(int i=0;i<n;i++) cout<<ans[i]<<' ';
	return 0;
}

寻找右边第一个小于它的数

寻找右边,我们可以换一种思想,将数组翻转,转换为寻找左边第一个小于自己的数 ,实际上不可能翻转,而是倒序遍历,因此变成了寻找一个数左边第一个小于它的数,于是归结为情形一。

#include<iostream>
#include<stack>
using namespace std;
const int Max=1e5+10;
int n,arr[Max],ans[Max];
stack<int> stk;
int main()
{
	cin>>n;
	for(int i=0;i<n;i++) cin>>arr[i];//arr完整数组
	for(int i=n-1;i>=0;i--)
	{
		while(!stk.empty()&&stk.top()>=arr[i]) stk.pop();//把栈内不符合条件的无用数据全部出栈(大于等于该元素),
		if(!stk.empty()) ans[i]=stk.top();//最后的栈顶就是答案。
		else ans[i]=-1;
		stk.push(arr[i]);
	}
	for(int i=0;i<n;i++) cout<<ans[i]<<' ';
	return 0;
}

寻找右边第一个小于它的数的下标

P5788 【模板】单调栈
题目描述

给出项数为 n n n 的整数数列 a 1 … n a_{1 \dots n} a1n

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

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

输入格式

第一行一个正整数 n n n

第二行 n n n 个正整数 a 1 … n a_{1\dots n} a1n

输出格式

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

样例 #1
样例输入 #1

5
1 4 2 3 5

样例输出 #1

2 5 4 5 0

【数据规模与约定】
对于 30 % 30\% 30% 的数据, n ≤ 100 n\leq 100 n100

对于 60 % 60\% 60% 的数据, n ≤ 5 × 1 0 3 n\leq 5 \times 10^3 n5×103

对于 100 % 100\% 100% 的数据, 1 ≤ n ≤ 3 × 1 0 6 1 \le n\leq 3\times 10^6 1n3×106 1 ≤ a i ≤ 1 0 9 1\leq a_i\leq 10^9 1ai109
AC代码

#include<iostream>
#include<stack>
using namespace std;
const int Max = 3e6 + 10;

int n,arr[Max], ans[Max];
stack<int> stk;

int main() 
{
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> arr[i];

    for (int i = n; i>=1; i--)
    {
        while (!stk.empty() && arr[stk.top()] <= arr[i]) stk.pop();
        if (!stk.empty()) ans[i] = stk.top();
        else ans[i]=0;//可不写,ans数组默认初始化全为0
        stk.push(i);
    }

    for (int i = 1; i <= n; i++) cout << ans[i] << ' ';

    return 0;
}

单调栈总结

①遍历顺序,找某个数的左边就正序遍历,右边就倒序遍历
②及时去掉无用数据的方式,如果找小于该元素,那就出栈中所有大于等于该元素的元素,此时的栈顶就是答案(栈不为空的情况下)。
③栈中存的是元素还是元素下标。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

毫无基础的人如何入门 Python ?

一、写在前面 其实不止是 Python&#xff0c;任何学习都会遇到这么几个难点 迷茫&#xff0c;对于学习过程中&#xff0c;对新的、未知的概念欠缺了解&#xff1b;彷徨&#xff0c;没有目的性的学习&#xff0c;不知道学完之后可以怎么用&#xff1b;乏力&#xff0c;知道很重…

通过 CMake 制作库文件 静态库 和 动态库

hehedalinux:~/Linux/loveDBTeacher-v2$ tree . ├── CMakeLists.txt ├── include │ └── head.h ├── main.c └── src├── add.c├── div.c├── mult.c└── sub.c CMake Calc 项目 在这里有add.c,div.c,mult.c,sub.c,main.c,head.h 二、生成静态库 …

protobuf学习日记 | 初识protobuf

目录 前言 一、序列化与反序列化 二、protobuf是什么 三、protobuf的使用特点 四、快速上手 1、proto文件编写 2、编译proto文件 3、序列化与反序列化的使用 前言 这是小编新开的一个栏目&#xff0c;为了记录自己在学习ProtoBuf的历程&#xff0c;也希望能帮助大家&am…

༺༽༾ཊ—设计-七个-07-原则-模式—ཏ༿༼༻

第七原则&#xff1a;迪米特职责 类与类之间的耦合度尽可能低 换言之&#xff0c;我们可以理解成———只与直接朋友说话&#xff0c;不跟陌生人说话 直接朋友&#xff1a; 通过方法传参传进来的朋友&#xff0c; 类自己的字段&#xff0c; 构造函数进来的也是直接朋友&…

CrystalDiskInfo v9.2.2

软件介绍 CrystalDiskInfo免费专业硬盘检测工具&#xff0c;硬盘健康状态信息检测工具。支持检测机械硬盘及固态硬盘信息&#xff0c;采用S.M.A.R.T.技术检测分析读取磁盘的详细信息&#xff0c;包含硬盘温度&#xff0c;固件、序列号、驱动器接口等。CrystalDiskInfo是由日本…

前沿重器[40] | 高级RAG技术——博客阅读

前沿重器 栏目主要给大家分享各种大厂、顶会的论文和分享&#xff0c;从中抽取关键精华的部分和大家分享&#xff0c;和大家一起把握前沿技术。具体介绍&#xff1a;仓颉专项&#xff1a;飞机大炮我都会&#xff0c;利器心法我还有。&#xff08;算起来&#xff0c;专项启动已经…

回声状态网络(Echo State Networks,ESN)详细原理讲解及Python代码实现

回声状态网络&#xff08;Echo State Networks,ESN&#xff09;详细讲解及Python代码实现 1 基本概念 回声状态网络是一种循环神经网络。ESN 训练方式与传统 RNN 不同。网络结构如下图&#xff1a; &#xff08;1&#xff09;储层&#xff08;Reservoir&#xff09;&#x…

zotero使用gpt

zotero使用gpt 下载 zotero下载&#xff1a;https://www.zotero.org/download/ 插件下载&#xff1a;https://github.com/MuiseDestiny/zotero-gpt?tabreadme-ov-file 插件安装 zotero中选择 工具->添加组件 选择右上角的齿轮&#xff0c;选择Install add-on from fil…

LabVIEW水泵性能测试系

项目背景&#xff1a; 水泵作为工业和日常生活中不可或缺的设备&#xff0c;其性能测试对于提高设计水平和改善性能至关重要。传统测试方法往往需要依赖经验和理论的结合&#xff0c;且测试效率和准确性有待提高。为了解决这些问题&#xff0c;设计了一套基于NI-LabVIEW平台的水…

TikTok电商加快闭环,独享IP为运营带来哪些好处?

近日有消息称TikTok电商在加快闭环&#xff0c;以后商家可能无法继续在TikTok上为其他电商平台或独立站引流了。如今“TikTok Shop Shopping Center”平台正在构建&#xff0c;将各种购物渠道整合为一体&#xff0c;这可能是一种趋势&#xff0c;意味着TikTok逐渐从社交应用转型…

movie-web, 开源的电影搜索网站

这个开源的电影网站 movie-web 看起来是一个很不错的项目。它提供了简洁易用的界面&#xff0c;并且能够保存播放进度和收藏电影。同时&#xff0c;它还支持中文输入和快速的搜索响应速度&#xff0c;这对于中文用户来说是非常方便的。 不过需要注意的是&#xff0c;虽然它可以…

C# Cad2016二次开发api(三)

直线 Line 属性中文数据类型作用Length长度double直线的长度Angle角度double直线的弧度&#xff0c;0~2πDelta增量Vector3d起点到终点的向量Normal法向向量Vector3d直线所在平面的法向单位向量Thickness厚度doubleEndPoint终点Point3d直线的终点StartPoint起点Point3d直线的起…

使用Python实现深度学习模型通常涉及以下几个步骤:

本人详解 作者:王文峰,参加过 CSDN 2020年度博客之星,《Java王大师王天师》 公众号:JAVA开发王大师,专注于天道酬勤的 Java 开发问题中国国学、传统文化和代码爱好者的程序人生,期待你的关注和支持!本人外号:神秘小峯 山峯 转载说明:务必注明来源(注明:作者:王文峰…

RabbitMQ脑裂处理

脑裂现象&#xff1a; Network partition detected Mnesia reports that this RabbitMQ cluster has experienced a network partition. There is a risk of losing data. Please read RabbitMQ documentation about network partitions and the possible solutions. 转载请在文…

什么是技术架构?架构和框架之间的区别是什么?怎样去做好架构设计?(二)

什么是技术架构?架构和框架之间的区别是什么?怎样去做好架构设计?(二)。 技术架构是对某一技术问题(需求)解决方案的结构化描述,由构成解决方案的组件结构及之间的交互关系构成。广义上的技术架构是一系列涵盖多类技术问题设计方案的统称,例如部署方案、存储方案、缓存…

Centos系统安全设置

1 设置密码复杂度&#xff0c;帐号密码有效期3个月 密码复杂度要求&#xff1a;最小长度8位&#xff0c;至少2位大写字母&#xff0c;1位小写字母&#xff0c;4位数字&#xff0c;1位特殊字符 1&#xff09;执行备份&#xff1a; #cp -p /etc/login.defs /etc/login.defs_bak…

逸学Docker【java工程师基础】3.3Docker安装nacos

docker pull nacos/nacos-server docker network create nacos_network #创建容器网络 docker run -d \ --name nacos \ --privileged \ --cgroupns host \ --env JVM_XMX256m \ --env MODEstandalone \ --env JVM_XMS256m \ -p 8848:8848/tcp \ -p 9848:9848…

Linux 有哪些搜索方式?5分钟带你搞懂!

5分钟带你掌握 Linux 的三种搜索方式 前言 1.find 命令 find 命令是用来在给定的目录下查找符合给定条件的文件 语法格式&#xff1a;find [查找起始路径] [查找条件] [处理动作] &#xff08;1&#xff09;根据名称查找&#xff1a;find [查找起始路径] -name 文件名 或者…

​LeetCode解法汇总82. 删除排序链表中的重复元素 II

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给定一个已排序的链表的头 head &#xf…

request entity too large 解决请求实体过大问题的方法

在网络请求过程中&#xff0c;有时会出现请求实体过大而导致服务器无法处理的情况。本文将介绍两种情况及其解决办法&#xff0c;真实可用&#xff01; 问题描述 请求实体过大问题主要分为两种情况&#xff1a; 1、带413状态码的请求实体过大 这种情况通常发生在请求文件过…
最新文章