一、单调栈原理
单调栈里装的是什么?
动态维护一个栈,把后续问题的答案都维持在这个栈中,把肯定不再有用的数字从栈中干掉。
二、理解与感悟
1、 动态维护,随进随维护,不是预处理。
2、可以将(O(N^2))的时间复杂度降为(O(N))。
3、此类“左侧(或右侧)最近”,比自己大的(或小的),用单调栈。
4、算法步骤:
(1)不管是向左看还是向右看,都先读到数组中。
(2)代码框架:
int n;
stack<int> stk; //装的是序号
const int N = 1010;
int a[N]; //a[stk.top()]是指栈顶元素的值
int res[N]; //装的是序号 a[res[i]]是真正的数值
for(int i=1;i<=n;i++){//从左到右
while(!stk.empty() && a[stk.top()]>=a[i]) {//栈顶元素找到了右侧第一个比自己小的
res[stk.top()]=i;
stk.pop();
}
stk.push(i);
}
for(int i=1;i<=n;i++){
if(res[i]) cout<<a[res[i]]<<" ";
else cout<<"-1 ";
}
三、左边第一个比我小
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
int a[N];
int stk[N], tt;
int res[N];//记录“左边比我小的数字”编号,值的描述:a[res[i]]
/**
测试用例:左边第一个比我小
6
6 10 3 7 4 12
结果:
-1 6 -1 3 3 4
*/
int main() {
//读入到数组,安全又健康
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
//从右向左遍历每一个数字,不断向左侧进发
// 注意这里的遍历方向发生了变化,因为我们是要找到左边比我小的元素的位置
for (int i = n; i >= 1; i--) {
//如果栈不空,并且栈顶元素大于遍历到的数字,那么这些数字就是已经找到了“左边比我小的数字”
while (tt && a[stk[tt]] > a[i]) {
res[stk[tt]] = i;//记录下谁是谁的“左边比我小的数字”,然后退群~
tt--;
}
//自己还没有找到“左边比我小的数字”,我还需要继续努力~
stk[++tt] = i;
}
//正序输出结果
for (int i = 1; i <= n; i++) {
//如果找到了它的“左边比我小的数字”,那么res[i]>0,否则就是没有找到
if (res[i]) cout << a[res[i]] << ' ';
else cout << "-1 ";
}
return 0;
}
四、左边第一个比我小(STL)
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
int a[N];
stack<int> stk;
int res[N];//记录“左边比我小的数字”编号,值的描述:a[res[i]]
/**
测试用例:左边第一个比我小
6
6 10 3 7 4 12
结果:
-1 6 -1 3 3 4
*/
int main() {
//读入到数组,安全又健康
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
//从右向左遍历每一个数字,不断向左侧进发
// 注意这里的遍历方向发生了变化,因为我们是要找到左边比我小的元素的位置
for (int i = n; i >= 1; i--) {
//如果栈不空,并且栈顶元素大于遍历到的数字,那么这些数字就是已经找到了“左边比我小的数字”
while (!stk.empty() && a[stk.top()] > a[i]) {
res[stk.top()] = i;//记录下谁是谁的“左边比我小的数字”,然后退群~
stk.pop();
}
//自己还没有找到“左边比我小的数字”,我还需要继续努力~
stk.push(i);
}
//正序输出结果
for (int i = 1; i <= n; i++) {
//如果找到了它的“左边比我小的数字”,那么res[i]>0,否则就是没有找到
if (res[i]) cout << a[res[i]] << ' ';
else cout << "-1 ";
}
return 0;
}
五、左边第一个比我大
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
int a[N];
int stk[N], tt;
int res[N];//记录“左边比我大的数字”编号,值的描述:a[res[i]]
/**
测试用例:左边第一个比我大
6
6 10 3 7 4 12
结果:
-1 -1 10 10 7 -1
*/
int main() {
//读入到数组,安全又健康
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
//从右向左遍历每一个数字,不断向左侧进发
// 注意这里的遍历方向发生了变化,因为我们是要找到左边比我小的元素的位置
for (int i = n; i >= 1; i--) {
//如果栈不空,并且栈顶元素大于遍历到的数字,那么这些数字就是已经找到了“左边比我大的数字”
while (tt && a[stk[tt]] < a[i]) {
res[stk[tt]] = i;//记录下谁是谁的“左边比我大的数字”,然后退群~
tt--;
}
//自己还没有找到“左边比我大的数字”,我还需要继续努力~
stk[++tt] = i;
}
//正序输出结果
for (int i = 1; i <= n; i++) {
//如果找到了它的“左边比我大的数字”,那么res[i]>0,否则就是没有找到
if (res[i]) cout << a[res[i]] << ' ';
else cout << "-1 ";
}
return 0;
}
六、左边第一个比我大(STL)
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
int a[N];
stack<int> stk;
int res[N];//记录“左边比我大的数字”编号,值的描述:a[res[i]]
/**
测试用例:左边第一个比我大
6
6 10 3 7 4 12
结果:
-1 -1 10 10 7 -1
*/
int main() {
//读入到数组,安全又健康
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
//从右向左遍历每一个数字,不断向左侧进发
// 注意这里的遍历方向发生了变化,因为我们是要找到左边比我小的元素的位置
for (int i = n; i >= 1; i--) {
//如果栈不空,并且栈顶元素大于遍历到的数字,那么这些数字就是已经找到了“左边比我大的数字”
while (!stk.empty() && a[stk.top()] < a[i]) {
res[stk.top()] = i;//记录下谁是谁的“左边比我大的数字”,然后退群~
stk.pop();
}
//自己还没有找到“左边比我大的数字”,我还需要继续努力~
stk.push(i);
}
//正序输出结果
for (int i = 1; i <= n; i++) {
//如果找到了它的“左边比我大的数字”,那么res[i]>0,否则就是没有找到
if (res[i]) cout << a[res[i]] << ' ';
else cout << "-1 ";
}
return 0;
}
七、右边第一个比我小
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
int a[N];
int stk[N], tt;
int res[N];//记录“右边比我小的数字”编号,值的描述:a[res[i]]
/**
测试用例:右边第一个比我小
6
6 10 3 7 4 12
结果:
3 3 -1 4 -1 -1
*/
int main() {
//读入到数组,安全又健康
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
//从左向右遍历每一个数字
for (int i = 1; i <= n; i++) {
//如果栈不空,并且栈顶元素大于遍历到的数字,那么这些数字就是已经找到了“右边比我小的数字”
while (tt && a[stk[tt]] > a[i]) {
res[stk[tt]] = i;//记录下谁是谁的“右边比我小的数字”,然后退群~
tt--;
}
//自己还没有找到“右边比我小的数字”,我还需要继续努力~
stk[++tt] = i;
}
//正序输出结果
for (int i = 1; i <= n; i++) {
//如果找到了它的“右边比我小的数字”,那么res[i]>0,否则就是没有找到
if (res[i]) cout << a[res[i]] << ' ';
else cout << "-1 ";
}
return 0;
}
八、右边第一个比我小(STL)
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
int a[N];
stack<int> stk;
int res[N];//记录“右边比我小的数字”编号,值的描述:a[res[i]]
/**
右边第一个比我小
测试用例:
6
6 10 3 7 4 12
结果:
3 3 -1 4 -1 -1
*/
int main() {
//读入到数组,安全又健康
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
//从左向右遍历每一个数字
for (int i = 1; i <= n; i++) {
//如果栈不空,并且栈顶元素大于遍历到的数字,那么这些数字就是已经找到了“右边比我小的数字”
while (!stk.empty() && a[stk.top()] > a[i]) {
res[stk.top()] = i;//记录下谁是谁的“右边比我小的数字”,然后退群~
stk.pop();
}
//自己还没有找到“右边比我小的数字”,我还需要继续努力~
stk.push(i);
}
//正序输出结果
for (int i = 1; i <= n; i++) {
//如果找到了它的“右边比我小的数字”,那么res[i]>0,否则就是没有找到
if (res[i]) cout << a[res[i]] << ' ';
else cout << "-1 ";
}
return 0;
}
九、右边第一个比我大
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
int a[N];
int stk[N], tt;
int res[N];//记录“右边比我小的数字”编号,值的描述:a[res[i]]
/**
测试用例:右边第一个比我大
6
6 10 3 7 4 12
结果:
10 12 7 12 12 -1
*/
int main() {
//读入到数组,安全又健康
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
//从左向右遍历每一个数字
for (int i = 1; i <= n; i++) {
//如果栈不空,并且栈顶元素小于遍历到的数字,那么这些数字就是已经找到了“右边比我大的数字”
while (tt && a[stk[tt]] < a[i]) {
res[stk[tt]] = i;//记录下谁是谁的“右边比我大的数字”,然后退群~
tt--;
}
//自己还没有找到“右边比我大的数字”,我还需要继续努力~
stk[++tt] = i;
}
//正序输出结果
for (int i = 1; i <= n; i++) {
//如果找到了它的“右边比我大的数字”,那么res[i]>0,否则就是没有找到
if (res[i]) cout << a[res[i]] << ' ';
else cout << "-1 ";
}
return 0;
}
十、右边第一个比我大(STL)
#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int n;
int a[N];
stack<int> stk;
int res[N];//记录“右边比我小的数字”编号,值的描述:a[res[i]]
/**
测试用例:右边第一个比我大
6
6 10 3 7 4 12
结果:
10 12 7 12 12 -1
*/
int main() {
//读入到数组,安全又健康
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
//从左向右遍历每一个数字
for (int i = 1; i <= n; i++) {
//如果栈不空,并且栈顶元素小于遍历到的数字,那么这些数字就是已经找到了“右边比我大的数字”
while (!stk.empty() && a[stk.top()] < a[i]) {
res[stk.top()] = i;//记录下谁是谁的“右边比我大的数字”,然后退群~
stk.pop();
}
//自己还没有找到“右边比我大的数字”,我还需要继续努力~
stk.push(i);
}
//正序输出结果
for (int i = 1; i <= n; i++) {
//如果找到了它的“右边比我大的数字”,那么res[i]>0,否则就是没有找到
if (res[i]) cout << a[res[i]] << ' ';
else cout << "-1 ";
}
return 0;
}