先来看一道例题:
给出一个整数数列 a1…n,求Ai右侧第一个比Ai大的数的下标。
朴素算法:(其实看不看无所谓)
#include <bits/stdc++.h>
using namespace std;
const int N = 3e6+5;
int n,a[N];
bool flag = true;
int main()
{
cin >> n;
for(int i=1;i<=n;i++) cin >> a[i];
for(int i=1;i<=n;i++){
flag = true;
for(int j=i+1;j<=n;j++){
if(a[j]>a[i]){
cout << j << ' ';
flag = false;
break;
}
}
if(flag) cout << 0 << ' ';
}
return 0;
}
然后就不出意外的出意外了,很明显会TLE
这时就可以使用单调栈来完成!
先讲一讲思路及原理:
顾名思义,单调栈就是在栈后进先出的基础上,具有单调的特性,又分为单调增栈和单调减栈,这里就以题目中的来进行讲解,当然如果是求与题目不相同的,相信作为大佬的你一定可以举一反三的吧!
以题目为样例讲解, 首先有一个空栈,我们从右往左的进行便利。
以下面的步骤对数操作 :
1.如果栈顶的元素大于(或小于,更具题目描述)当前的操作数(在栈不为空的情况下,如果栈为空就直接入栈),就弹出栈顶的元素,重复操作2,直到不满足。
2.记录当前的栈顶元素(在栈不为空的情况下),这个元素就是操作数的目标元素。
3.将当前的数入栈。
来看下样例:
注:如果我们直接将元素的值入栈,就只能知道大于他元素是什么,但在做题中的大多数情况下这是不够的,我们就还需要O(n)查找元素的位置,那单调栈还有什么意义?
于是我们就可以进行优化,入栈元素的下标,在判断时只需要用变量套一层就好了。
看代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 3e6+5;
int a[N];
int ans[N]; // 记录答案的数组
int n;
stack<int> stk_r;
int main()
{
cin >> n;
for(int i=1;i<=n;i++) cin >> a[i];
for(int i=n;i>=1;i--){ // 从右往左进行遍历
while(!stk_r.empty()&&a[stk_r.top()] <= a[i]) stk_r.pop();
//打破单调性,弹出元素
//注意1:因为栈中存入的是下标,所以在外面套一层a[]
//注意2:因为C++是从左往右进行判断,所以判断不为空应该写在前面
//防止stk_r.top()越界导致RE
//别问我是怎么知道了:(
if(!stk_r.empty()){ // 如果不为空记录元素
//如果为空说明没有比他大的元素,默认为0
ans[i] = stk_r.top(); // 记录下标
}
stk_r.push(i); // 入栈
}
for(int i=1;i<=n;i++){
cout << ans[i] << ' ';
}
return 0;
}
下面是没有注释的代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 3e6+5;
int a[N];
int ans[N];
int n;
stack<int> stk_r;
int main()
{
cin >> n;
for(int i=1;i<=n;i++) cin >> a[i];
for(int i=n;i>=1;i--){
while(!stk_r.empty()&&a[stk_r.top()] <= a[i]) stk_r.pop();
if(!stk_r.empty()) ans[i] = stk_r.top();
stk_r.push(i);
}
for(int i=1;i<=n;i++){
cout << ans[i] << ' ';
}
return 0;
}
这有一道练习题:发射站 - 洛谷
题目思路:明显模板题跑两边,可以从左向右再跑一遍。
AC code:
#include <bits/stdc++.h>
using namespace std;
const int N = 1e6+5;
stack<int>l;
stack<int>r;
struct bb{
long long h,e;
long long j = 0;
}a[N];
long long n;
int main()
{
cin >> n;
for(int i=1;i<=n;i++) cin >> a[i].h >> a[i].e;
for(int i=1;i<=n;i++){
while(!l.empty() && a[l.top()].h <= a[i].h) l.pop();
if(!l.empty()){
a[l.top()].j += a[i].e;
}
l.push(i);
}
for(int i=n;i>=1;i--){
while(!r.empty()&&a[r.top()].h<=a[i].h) r.pop();
if(!r.empty()){
a[r.top()].j += a[i].e;
}
r.push(i);
}
long long maxn = INT_MIN;
for(int i=1;i<=n;i++){
// cout << a[i].j << ' ';
maxn = max(maxn,a[i].j);
}
// cout << endl;
cout << maxn;
return 0;
}