单调栈+单调队列

单调栈

例题–单调栈
求当前数之前的第一个小于当前数的数
解释一下这题的思路:
如果暴力查找

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++;
找窗口的最大值类似,不写了。

简单理一下步骤:
1.队列不为空(beg<=end)看队头元素是否不在窗口内部,如果不在扔掉就队头元素
(队头元素是上一个窗口内最小的元素)
if (beg <= end && q[beg] < i - k + 1) beg++;
2.向队列中插入新元素,如果新元素比队尾元素小就一直把队尾元素抛出
while (end >= beg && a[i] <= a[q[end]]) end--;
3.向队尾插入新的元素
q[++end] = i;
4.判断是否可以输出最小元素(队头元素)
if (i>=k-1) cout << a[q[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++)
	{
    
    
        //bge<=end表是队列里面有元素
		if (beg <= end && q[beg] < i - k + 1) beg++;
		//beg,end 对应的是队列的首尾
		//移动窗口扔掉的数据是不在窗口的数,是上一段窗口的最小值
		//在q[beg]之前且大于a[q[beg]],早在a[q[beg]]插入队列之前就被丢掉了
		//a[i]是移动窗口后新加入的元素
		while (end >= beg && a[i] <= a[q[end]]) end--;
		//跳出循环说明a[i]小于a[q[end]]或者元素全被清掉了 ,所以插入q[++end]=i;
		//如果是后一种情况,a[q[end]]比当前a[i]更有资格作当前窗口的最小值
		//此时保证数组a中下标为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;
}

猜你喜欢

转载自blog.csdn.net/weixin_50816938/article/details/118978088