文章就当做算法学习日记啦,用来记录学习过程中的一些感受。
本人比较菜,但创作不易,各位大佬勿喷。
栈与队列可以说是c++ STL中很常见的了。简单来讲的话:
栈就是元素先入后出,队列就是元素先入先出。
有一种操作为数组模拟栈与队列,这样会能够更加容易得实现对元素的操作,还有一部分原因是他的速度也会快一些。
在讲单调栈与单调队列之前,先了解一下用数组模拟栈与队列。
数组模拟栈栈模拟的话首先需要一个数组用来存储栈的值,这里我们定义为stk[N], 然后需要一个整型变量记录栈顶,我们定义为tt,初始化为0,相当于现在栈内没有元素。对栈的操作概括为以下四种:
向栈内添加一个元素int x; cin>>x; stk[++tt] = x;删除栈顶元素
tt--;检验栈内元素是否为空
即对tt进行判断,因为tt就是栈内元素的个数
bool check()
{
if(!tt)
return true;
else
return false;
}
访问栈顶元素
void query()
{
cout<
讲完这些后就对数组模拟栈有了初步的认识,接下来说一说单调栈:
单调栈
顾名思义,单调栈就是栈内的元素呈现单调性.....感觉比较抽象,那一个典型题来说一下:
对于一个长度为n的数组,输出每一个元素左侧比他小且距离他最近的元素,若没有则输出 - 1 。
通俗的做法就是二重暴力循环,利用双指针进行遍历,时间复杂度较高,这时候可以用单调栈进行简单化:
试想一下,对于每一个元素,他左侧的所有元素可以看成一个栈。要找出他左侧最小且离他最近的元素,我们想一下有没有元素是永远不可能是他的答案的呢?
经过思考可以得到,例如假设一种情况:对于a[7]来说,a[3] > a[5],这时候虽然a[5]与a[7]的大小未知,但我们可以确定a[3]一定不是答案,因为对于后续所有元素若a[3]比较小,那a[5]也一定小并且a[5]离得更近。
因此我们可以得出通常情况,每读入一个x,就判读他与栈顶元素的关系,若栈顶元素更小一些,那栈顶元素就是答案,并将x插入到栈顶; 若栈顶元素更大的话,栈顶元素一定不是答案,并且由于x更小,对于后续所有元素的答案都不可能是他,因此把栈顶元素删去,如此循环直至栈顶元素小于x,执行第一种情况,这样结束后的栈元素呈现递增性。
代码如下:
#include
#include
#include
using namespace std;
const int N = 100010;
int stk[N];
int tt = 0, m;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>m;
while(m--)
{
int x;
cin>>x;
while(tt && stk[tt] >= x)
tt--;
if(tt)
cout<
数组模拟队列
数组模拟队列,我们仍需一个数组来储存队列的值,由于先进先出,需要一个处理队头,我们定义为hh,初始化为0,一个处理队尾,我们定义为tt,定义为-1。那么初始是hh > tt;
则说明栈内为空。对于栈的操作大致分为以下几种:
向队尾插入一个数
void push_x(int x)
{
que[++tt] = x;
}
从队头弹出一个数
void pop()
{
hh++;
}
判断队列是否为空
bool judge_empty()
{
if(hh <= tt)
return false;
else
return true;
}
查询队头元素
void query()
{
cout<
在理解数组模拟队列后,我们来引入单调队列:
单调队列
顾名思义,就是有单调性的队列,由于存在对头与队尾,因此可以控制队列内元素的个数。
还是通过一道典型题来说:
给定一个大小为n ≤ 10^6 的数组。
有一个大小为 k 的滑动窗口,它从数组的最左边移动到最右边。
你只能在窗口中看到 k 个数字。
每次滑动窗口向右移动一个位置。
输出每次窗口内最小值。
给出一个样例理解一下题意:
数组为 5 9 6 3 4, k = 3;
三次窗口就分别为:5 9 6;9 6 3; 6 3 4;
故最小值分别为5 3 3.
对于本道题,朴素做法还是二重循环,每次在第二次循环时,每次进行固定次数的遍历。但想一想会发现,还是会存在一定不会是答案的元素的。例如: 当后输入的数比前输入的数小时,那前一个数一定不会是答案,因为对于滑动窗口来说,后一个数从此时开始存在的时间一定会大于前一个输入的数的,因此前一个数一定不再会是答案了。因此就用队列来储存,队列就是一个递增的队列,每次输出队头元素即可。
但为了保证队头一定在窗口内,用队列储存下标与i - k + 1比较比较好,每次循环时进行判断再将窗口向后移动。
代码如下:
#include
#include
#include
using namespace std;
const int N = 1000010;
int a[N], que[N];
int hh = 0, tt = -1;
int n, k;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin>>n>>k;
for(int i = 0 ; i < n ; i++)
cin>>a[i];
for(int i = 0 ; i < n ; i++)
{
while(hh <= tt && i - k + 1 > que[hh])
hh++;
while(hh <= tt && a[que[tt]] >= a[i])
tt--;
que[++tt] = i;
if(i - k + 1 >= 0)
cout<



