单调栈笔记及例题详解

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

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

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

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);
        }
    }
}
相关推荐
jimy13 小时前
虚函数和 RTTI实现运行时多态
开发语言·c++
Pniubi3 小时前
力扣41缺失的第一个正数(哈希表法)
算法·leetcode·职场和发展
地平线开发者3 小时前
MQBench QAT量化实践指南
人工智能·算法·自动驾驶
今夜有雨.3 小时前
图像运算、掩膜/ROI 与绘制交互
c语言·数据结构·c++·qt·算法·计算机视觉
UIU1143 小时前
P1028 [NOIP 2001 普及组] 数的计算
c++·算法
sbjdhjd4 小时前
智能体开始“动手”之后:OpenAI越权事件、Anthropic算力资本化与开放权重模型竞逐 | AI与SI行业日报整理(9月29日—10月6日)
大数据·人工智能·经验分享·笔记·ai·chatgpt·开源
上位机妹子4 小时前
从零理解 EF Core:用 Minimal API 给「工作项看板」建模(小白笔记)
笔记
Keven_114 小时前
BUG:UVA116
算法·bug·图论
Henry-SAP4 小时前
Beam大模型开源引爆AI热潮
算法
深圳老胡4 小时前
STM32 MCU 国产替代型号简介
笔记·stm32·单片机·代码规范