单调栈+单调队列
单调栈
例题–单调栈
求当前数之前的第一个小于当前数的数
解释一下这题的思路:
如果暴力查找
for (int i = 0; i < n; i++){
int sign = 0;
cin >> a[i];
for (int j = i - 1; j >= 0; j--){
if (a[j] < a[i]){
sign = 1;
cout << a[j];
break;
}
if (sign == 0) cout << -1;
cout << " ";
}
}
比如3 4 2 7 5 当i指向a[4]==5时,j从a[3]开始往前移动,a[3]==7,j继续移动,a[2]==2成立,7向前的第一位小于数是2,5向前的第一位小于数是2,这里就可以发现7对5并没有意义,如果在该数组后面继续增加数比如3 4 2 7 5 4,4的第一个小于数也是2;
这里引入单调栈
对于7来说2是小于它的第一位数,因为5>7,所以5在考虑小于它的第一位数就可以忽略掉7,4<5,所以它可以忽略掉7 5,
a[i],a[j] (j>i) 小于a[j]的第一位数要么就是a[i],如果a[i]<a[j],要么就是小于a[i]的第一位数(a[i]>=a[j]),而小于a[i]的第一位数已经被保存在a[i-1],所以只要递推就好
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
int main()
{
ll a[100005];
ll n, x;
cin >> n;
int p = -1;
for (ll i = 0; i < n; i++)
{
cin >> x;
while (p!=-1 && a[p] >= x) p--;
//找到x往前第一个小于x的数,后面的数直接在++p的基础上更新就行,原因就是上面那段加粗字
if (p!=-1) cout << a[p];
else cout << -1;
cout << " ";
a[++p] = x;
}
return 0;
}
单调队列
例题–滑动窗口
我们设两个指针beg和end,beg指向队头,end指向队尾
队列的意义,存取当前区间的最小值(按顺序),这里思路和单调栈是一样的
当存入新的数据的时候,将新数据与队尾元素进行比较,如果新数据比队尾元素要小就说明对于这一块区域,在新数据之前比新数据大的元素没有作业,扔掉
当移动窗口的时候要判断beg的指针是否也需要移动if(beg<=end&&q[beg]<i-k+1) beg++;
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e6 + 5;
int a[N], q[N];//q存的是队列里元素的下标
int main()
{
int n, k;
cin >> n >> k;
for (int i = 0; i < n; i++)
cin >> a[i];
int beg = 0, end = -1;
for (int i = 0; i < n; i++)
{
if (beg <= end && q[beg] < i - k + 1) beg++;
//移动窗口扔掉的数据就是最小值
//在q[beg]之前且大于a[q[beg]],早在a[q[beg]]插入队列之前就被丢掉了
while (end>=beg&&a[i] <= a[q[end]]) end--;
//跳出循环说明a[i]小于a[q[end]] ,所以插入q[++end]=i;
//此时保证beg--end所有数都在队列内
q[++end] = i;
if (i>=k-1) cout << a[q[beg]] << " ";
}
cout << endl;
beg = 0, end = -1;
for (int i = 0; i < n; i++)
{
if (beg <= end && q[beg] < i - k + 1) beg++;
while (end>=beg&&a[i] >= a[q[end]]) end--;
q[++end] = i;
if (i >= k - 1) cout << a[q[beg]] << " ";
}
return 0;
}
浙公网安备 33010602011771号