【单调栈】
单调栈
1.0 原始模型
描述:给定一个序列,求出序列每个位置在左侧距离其最近的,且比起小的数,如果不存在则返回-1,存在则返回其值, 最后输出该结果。
解析:
首先考虑对该题目的朴素解(暴力解法),挖掘一些性质,把求解空间进一步缩小,从而降低问题的求解时间复杂度。
1. 暴力做法
双重循环:第一重循环,遍历序列上所有的位置i;
第二重循环,从第一重循环的位置i开始,向左遍历,直到找到第一个比位置i处的值,否则输出-1,表示不存在这样的小值;
伪码如下:
for i = 0; i < n; i++
for j = i- 1; j >= 0; j--
if (a[i] > a[j]) {
cout << a[j] <<endl;
break;
}
cout << -1 << endl;
2.分析特性
可以观察,当我们通过一些数据结构,可以保留之前遍历过的所有的元素,对于给定位置i, 那么假设在i的左侧存在两个位置j,k,满足
a[j] >= a[k] (j < k),因为是需要找左侧最近,所以这时选择k位置比j位置更优,对于位置i,位置k更优,因此可以舍去位置j。
因为上面实际上是逆序的情况,要删除这种逆序情况,这样得到的序列是严格递增的。因为我们在比较时,总是要比较左侧最近的点,因此需要使用栈的数据结构来
使用。
3.代码实现
stack st;
int a[N];
for (int i = 0; i < n; i++)
{
//对于位置i,需要考察期左侧的栈序列;
while (!st.empty() && st.top() >= a[i])
{ //栈非空,且比栈顶元素小,说明其可以取代栈顶成为更优解;
st.pop();
}
//此处要么栈为空,或者当前元素比栈顶大;
if (!st.empty())
{
//此时栈非空,那么栈顶就是其左侧比当前值还小的元素;
cout << st.top() << endl;
} else {
//栈为空,说明左侧没有符合的值了,这个时候输出特殊情况处理;
cout << -1 <<endl;
}
//将当前元素添加进去,因为在当前元素之前已经保持了严格递增,将当前元素添加进去后,单调性还是保持不变
//因为前面的if语句已经保证了,从而可以保证该性质可以求解。
st.push(a[i]);
}
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
//这里采用的是数组模拟栈的方式,完全可用STL的方式来写,只不过会增加一些运行时间;
int n;
int stk[N], tt;
int main()
{
cin >> n;
for (int i = 0; i < n; i++) {
int x;
cin >> x;
while (tt && stk[tt] >= x) tt--;
if (tt) cout << stk[tt] <<" ";
else cout << -1 <<" ";
stk[++tt] = x;
}
cout <<endl;
return 0;
}
1.1 扩展
类似的,还有寻找左侧最近且最大的,右侧最近最小/最大的问题。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】