单调栈笔记及例题详解

单调栈:栈中加入顺序性,元素是严格单调递增或递减的。

但是说清楚方向: 从栈底到栈顶的递增栈,还是从栈顶到栈底的递增栈

具体操作:(以底到顶递减为例)

1.如果新的元素比栈顶元素小,入栈

2.如果新的元素比栈顶元素大,就一直把栈内元素弹出来,直到栈顶比新元素大,新元素入栈

应用:多用于求解元素的左右大小边界问题

eg 1 5 4 2 3 7 9 那么我要找到每个数向左第一个比自身大的数 O(n^2)

用单调栈来优化:

元素间大小判断:对于当前遍历到的数,栈顶为答案。

1.向左找第一个比自身大的数。 左->右遍历数组 模型:底到顶的递减栈---怎么记:找大的,就保证栈底最大

首先明确,栈满足后进先出,所以栈顶元素一定就是离当前元素最近的元素

可以得出的结论是:(1)如果ai入栈时,栈为空,说明ai左边没有比它大的数

(2) 用递减栈来保存已经遍历过的数,当枚举到第i个数时,栈中的数就是可能比第i个数大的数

这个和栈顶比较。栈顶比i大 栈顶就是答案,否则就连续出栈,知道一个栈顶比i大,

此时记录答案,再把i入栈

Q:栈用来保存已经遍历过的数,但是栈遍历过程中是会执行出栈操作的,意味着 栈中并没有保存所有的遍历

过的数,不会漏掉答案吗?

不会 因为出栈的数x1,x2都是比当时新的栈顶y小的数,那么现在遍历到i

假设x1,x2,y>1 由于y离i更近,所以x1,x2一定不会是答案,所以即便是x1 x2没在栈中也无妨

记录每个入栈元素的答案

1.向左找第一个比自身大的数。 左->右遍历数组 模型:底到顶的递减栈---怎么记:找大的,就保证栈底最大

2.向左找第一个比自身小的数。 左->右遍历数组 模型:底到顶的递增栈---怎么记:找小的,就保证栈底最小

3.向右找第一个比自身大的数。 右->左遍历数组 模型:底到顶的递减栈---怎么记:找大的,就保证栈底最大

别找错方向了!!

4.向左找第一个比自身大的数。 右->左遍历数组 模型:底到顶的递增栈---怎么记:找小的,就保证栈底最小

代码编写

写法二:记录每个出栈元素(栈顶)的答案

1.向左找第一个比自身大的数。 右->左遍历数组 模型:底到顶的递减栈---怎么记:找大的,就保证栈底最大

因为在栈里面的数据在右边,而要入栈的元素才在左边

(1)当栈为空时,直接入栈

(2)当遍历到ai时,如果ai>栈顶, 就要连续出栈, 此时ai就是连续出栈的数的答案

栈顶出栈时记录栈顶的的答案 否则ai<=栈顶 , ai直接入栈

(3)当遍历完后,若此时栈非空,战中的数据就没有答案, 连续出栈 把答案记录为-1

例题一:

时间复杂度为O(n),每个元素入栈和出栈各一次

代码中的关键点

  • while循环仅弹出比当前元素 arr[i]大或等于的栈顶元素。
  • 每个元素被弹出后,不会再次入栈(因为后续元素无法满足 arr[k] >= arr[j]的条件)。
  • 因此,所有元素的总弹出次数等于入栈次数,均为 n次。
cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int arr[3000005];
int ans[3000005];

int f(int i)
{
  return ans[i];
}
int n;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++)
    {
      cin>>arr[i];
  }
  stack<int>s;
  
  for(int i=n;i>=1;i--)  //从右往左遍历 
  {
    while(!s.empty()&&arr[i]>=arr[s.top()]) //等于都不行,这里加入等号时会进入循环,把栈顶元素踢掉 
    {
      s.pop();
    }
    //循环退出,判断一下
    if(s.empty())
    {
      ans[i]=0;
      s.push(i);
    }
    
    else
    {
      ans[i]=s.top();
      s.push(i);
    }
    
  }
  
  //cout<<"\n下标为:";
  for(int i=1;i<=n;i++)
  {
    cout<<f(i)<<" ";
  }
}

例题2https://www.luogu.com.cn/problem/P15150#ide

cpp 复制代码
void left_stack() //左找最大
{
    stack<int> st; //构造栈底大,栈顶小
    for(int i=1;i<=n;i++)
    {
        while(!st.empty()&&H[st.top()]<=H[i])  // 不为空并且栈顶比较小
        {
           st.pop();
        }
        if(st.empty())
            {
                dis[i]=INT_MAX;
                st.push(i);
            }
        else
        {
            dis[i]=abs(i-st.top());
            st.push(i);
        }
    }
}


void right_stack() //右找最大
{
    stack<int> st; //构造栈底大,栈顶小
    for(int i=n;i>=1;i--)
    {
        while(!st.empty()&&H[st.top()]<=H[i])  // 不为空并且栈顶比较小
        {
           st.pop();
        }
        if(st.empty())
            {
               // dis[i]=min(dis[i],);  可以不管 
                st.push(i);
            }
        else
        {
            dis[i]=min(dis[i],abs(i-st.top()));
            
            st.push(i);
        }
    }
}
相关推荐
流浪00118 分钟前
C/C++后端筑基系列(二):C++ 从基础到进阶
开发语言·c++
kyle~19 分钟前
x86 汇编LOCK前缀 --- 硬件架构向软件提供的核心同步原语
汇编·c++·性能优化·硬件架构·实时系统
额,不知道写啥。1 小时前
从区间加一次函数到区间加多次函数最值-----区间数据结构与函数的一些东西(《浅谈函数最值的动态维护》的学习笔记)
数据结构·笔记·学习
LuminousCPP1 小时前
数据结构-二叉树(五):查找、销毁与前序序列建树
c语言·数据结构·笔记·二叉树
Escalating_xu2 小时前
【C++入门基础(下)】默认参数、函数重载、引用、inline 与 nullptr
android·c++·redis
艾莉丝努力练剑2 小时前
【AI大模型接入SDK】Provider分析与实现
c++·人工智能·学习·面试·大模型·llm
土司大王8 小时前
LeetCode hot100——对称二叉树
算法·leetcode·职场和发展
sali-tec9 小时前
C# 基于OpenCv的视觉工作流-章106-差值追踪
图像处理·人工智能·opencv·算法·计算机视觉
Nontee9 小时前
MySQL 插入冲突了怎么办?两种处理方式入门笔记
数据库·笔记·mysql