--- title: "单调栈" created: 2025-11-28 tags: - 算法 --- # 单调栈 ## 单调栈 找最近 **用于找每个数 左/右边 最近的 比它小/大 的数** ### **(1)左边 最近的 小于它 的数** 从1~n遍历 序列应该是单调递增(保证栈头为最优解) 所以如果出现向下趋势 就不断出栈 ```cpp while(tt && x <= stk[tt]) tt--; //具体操作 //…… //把栈头作为答案保存 stk[++tt]=x;//把该元素入栈 ``` ![[image-150c5552.png]] ### **(2)左边 最近的 大于它 的数** 从1~n遍历 序列应该是单调递减 所以如果出现向上趋势 就不断出栈 ```cpp while(tt && x >= stk[tt]) tt--; //具体操作 //…… //把栈头作为答案保存 stk[++tt]=x;//把该元素入栈 ``` ### **(3)右边 最近的 小于它 的数** 从n~1遍历 序列应该是单调递增 所以如果出现向下趋势 就不断出栈 ```cpp for(int i=n;i>=1;i--){ while(tt && h[i] <= h[stk[tt]]) tt--; //具体操作 //…… //把栈头作为答案保存 stk[++tt]=i;//把该下标入栈 } ``` ### **(4)右边 最近的 大于它 的数** 从n~1遍历 序列应该是单调递减 所以如果出现向上趋势 就不断出栈 ```cpp for(int i=n;i>=1;i--){ while(tt && h[i] >= h[stk[tt]]) tt--; //具体操作 //…… //把栈头作为答案保存 stk[++tt]=i;//把该下标入栈 } ``` 想清楚要找什么 考虑遍历方向 和答案应该是较大的还是较小的 较小的话 栈里应该是单调递增 这样才能保证栈顶是最合适的答案 较大的话 反过来 栈里应该是单调递减 ## 单调栈+二分 找最远 ### (1)左边 最远的 大于它 的数 比如 对于3来说 如果左边已经有了5 在53中间再来一个4是完全没有必要的 因为3肯定找到的是那个5 因为更大 且更远 由此发现是一个不出现递减趋势的序列 但个53中间有个7的话 这个7虽然不会被3找到 但是它可能会被后面的一个6找到 综上 构造一个单调递增的序列 若这个新加的数能保持递增趋势 说明不存在比它更大的数 没必要去找了 直接加进去 供后面的数去找 如果不能保持递增趋势 就说明左边有一个比它更大的数 在这个已有的单调序列中二分找到 ```cpp #include using namespace std; const int N=100; int a[N]; vector stk; int ans[N]; int n; int main() { cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; //需要注意的一点是 stk存的是下标 for(int i=1;i<=n;i++){ //a[i]>a[stk.back()] 还是 a[i]>=a[stk.back()] //加不加等号看题意 //找严格小于它的数的话 这里加等号 意味着找到它自己 //找前面小于等于它的数的话 这里不加等号 if(stk.empty() || a[i]>=a[stk.back()]){//发现该元素比栈顶元素还大(保持单调递增) stk.push_back(i);//直接入栈 ans[i]=i;//记录答案 可能求长度等等 } else{//发现该元素不是最大 那么原单调序列里肯定存在一些比他大的数 二分找到最远的那个 int l=1,r=stk.size()-1; while(l>1; if(a[stk[mid]]>a[i]) r=mid; else l=mid+1; } ans[i]=stk[r]; } } for(int i=1;i<=n;i++) cout< using namespace std; const int N=100; int a[N]; vector stk; int ans[N]; int n; int main() { cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++){ if(stk.empty() || a[i]>1; if(a[stk[mid]] using namespace std; const int N=100; int a[N]; vector stk; int ans[N]; int n; int main() { cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=n;i>=1;i--){//只需改循环 if(stk.empty() || a[i]>a[stk.back()]){ stk.push_back(i); ans[i]=i; } else{ int l=0,r=stk.size()-1; while(l>1; if(a[stk[mid]]>a[i]) r=mid; else l=mid+1; } ans[i]=stk[r]; } } for(int i=1;i<=n;i++) cout< using namespace std; const int N=100; int a[N]; vector stk; int ans[N]; int n; int main() { cin>>n; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=n;i>=1;i--){ if(stk.empty() || a[i]>1; if(a[stk[mid]]