一、我们首先要了解栈的概念 在 c++中有着 STL 的存在可以方便我们做很多事,而 栈(stack)就是其中的一部分,简单的说栈就是一种 先进后出 的数据机构
通过思考和模拟栈的结构,发现我们可以用 一维数组 和一个变量 tt 来实现栈这一数据结构
stk[100010] 用来代表栈 tt 代表一种指针
栈的读入操作
// add insert
int x;
cin>>x;
stk[++tt] = x; // 我这里是把栈的下标从 1 开始 用来与队列进行区别
// 这样我们就已经把 x 读入到了我们的模拟栈中
栈的弹出操作
// remove
tt--;//这样就已经把队头的元素弹出了
因为栈是一个类似于木桶的存在,同时 tt 又一直指向木桶的最上方;所以当 tt-- 时,位于最上方的元素就会被弹出
这道题来自于AcWing第830题 也是有关单调栈的应用
给定一个长度为 NN 的整数数列,输出每个数左边第一个比它小的数,如果不存在则输出 −1−1。
输入格式
第一行包含整数 NN,表示数列长度。
第二行包含 NN 个整数,表示整数数列。
输出格式
共一行,包含 NN 个整数,其中第 ii 个数表示第 ii 个数的左边第一个比它小的数,如果不存在则输出 −1−1。
数据范围
1≤N≤1051≤N≤105
1≤数列中元素≤1091≤数列中元素≤109输入样例:
5 3 4 2 7 5
输出样例:
-1 3 -1 2 2
这个是题解
#include <iostream>
using namespace std;
const int N = 1e5 +10;
int stk[N],tt;
int a[N];
int main(void)
{
int n;
cin>>n;
for(int i = 0;i<n;i++)
{
while(tt && stk[tt] > a[i]) tt--; // 这里会遍历数组里面的元素 把大于该值的数弹出
// 因为题意里面找的是第一位小于 a[i] 的值 如果
// 如果上一个值是大于 a[i] 的话那么接下来的值都
// 不会选择该值
if(!tt) cout<<"-1";
stk[++tt] = x;
}
}
下面来模拟队列,队列也是 STL 中的一个容器,他和栈的区别是先进先出,类似于水管一样,
也就是从队尾进入,从对头弹出
同样,通过对队列的模拟我们也可以用数组来实现队列的功能
q[100010] , tt , hh;
hh 代表队头
tt 代表队尾
队列的读入操作
// add
hh = 0,tt = -1;// 这里我们将队列的下标设置为从一开始 与 栈 进行分开
int x;
cin>>x;
q[++tt] = x;// 从队尾插进了元素
队列的弹出操作
// remove
hh++;
这里弹出和 栈 的弹出也是有些相似,hh 相当于在一条水管中的上部分,tt 相当于下部分
当 hh++ 时就相当于 hh 往下移动,因此前面位于 hh 的元素 q[hh] 就会被弹出
这道题来自于AcWing第154题的滑动窗口问题
给定一个大小为 n≤106n≤106 的数组。
有一个大小为 kk 的滑动窗口,它从数组的最左边移动到最右边。
你只能在窗口中看到 kk 个数字。
每次滑动窗口向右移动一个位置。
以下是一个例子:
该数组为
[1 3 -1 -3 5 3 6 7]
,kk 为 33。
窗口位置 最小值 最大值 [1 3 -1] -3 5 3 6 7 -1 3 1 [3 -1 -3] 5 3 6 7 -3 3 1 3 [-1 -3 5] 3 6 7 -3 5 1 3 -1 [-3 5 3] 6 7 -3 5 1 3 -1 -3 [5 3 6] 7 3 6 1 3 -1 -3 5 [3 6 7] 3 7 你的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。
输入格式
输入包含两行。
第一行包含两个整数 nn 和 kk,分别代表数组长度和滑动窗口的长度。
第二行有 nn 个整数,代表数组的具体数值。
同行数据之间用空格隔开。
输出格式
输出包含两个。
第一行输出,从左至右,每个位置滑动窗口中的最小值。
第二行输出,从左至右,每个位置滑动窗口中的最大值。
输入样例:
8 3 1 3 -1 -3 5 3 6 7
输出样例:
-1 -3 -3 -3 3 3 3 3 5 5 6 7
下面是题解
#include <iostream>
using namespace std;
const int N = 1e6 +10;
int q[N];
int a[N];
int hh,tt = -1;
int main(void)
{
int n,k;
cin>>n>>k;// n 代表数组长度 k 代表窗口长度
for(int i = 0;i<n;i++) cin>>a[i];
for(int i = 0;i < n;i++)
{
if(hh <= tt && i - k + 1>q[hh]) hh++;
while(hh <= tt && a[q[tt]] >= a[i]) tt--; // 这里是把所有大于 a[i] 的元素都弹了出去
//因为当队列中的最后进来的如果比前面的都
//小的话,前面的所有数值就都会用不到,所
//以把没用的元素都弹了出去,下面求大值的
//时候也是一个道理
q[++tt] = i;
if (i >= k - 1) printf("%d ", a[q[hh]]);
}
puts("");
hh=0,tt= -1;
for(int i = 0;i<n;i++)
{
if(hh <= tt && i-k+1>q[hh]) hh++;
while(hh <= tt && a[q[tt]] <= a[i]) tt--;
q[++tt] = i;
if (i >= k - 1) printf("%d ", a[q[hh]]);
}
cout<<endl;
}
有些人可能会有疑惑,明明可以用STL,我们为什么又要费心思去模拟他呢,这是因为,模拟STL可以省出很多时间,优化程序的效率,这一点在做算法题目的时候尤为重要