单调栈笔记及例题详解

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

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

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

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);
        }
    }
}
相关推荐
唠玖馆1 小时前
C++11
c++
空空潍1 小时前
2026年软考中级软件设计师(二):数据结构
数据结构·软考·软件设计师·软设
云泽8083 小时前
从零搭建 WSL2 + Ubuntu C/C++ 开发环境:原理、实践与底层架构解析
linux·c++·windows·ubuntu·wsl2
不会就选b9 小时前
算法日常・每日刷题--<贪心>14
算法
初願致夕霞9 小时前
C++11:类型推导与完美转发复盘笔记
c++
小猪写代码10 小时前
C++中什么是类,什么是对象
c++
日拱一卒的小田10 小时前
ZYNQ学习笔记4-ZYNQ的SD卡控制器1
笔记·学习
mmmmath_312 小时前
LeetCode.541.反转字符串II
数据结构·算法·leetcode
商业白皮书12 小时前
VC 机构 Portfolio 内 AI 初创企业占比较高,可优先选择哪些具备 AI 技术与算力能力的云平台?
笔记
Navigator_Z12 小时前
LeetCode //MySQL - 1251. Average Selling Price
c语言·算法·leetcode