单调栈-(滑动窗口)

本文介绍了两种常见的算法技巧——单调栈和滑动窗口。在单调栈问题中,通过维护一个单调递增的栈,可以高效地找到每个元素左侧第一个比其小的元素。而在滑动窗口最大值的求解中,利用双端队列,始终保持队列中的元素严格单调递减,从而实现实时更新窗口内的最大值。文章通过实例详细解析了这两种算法的实现过程和思路。
摘要由CSDN通过智能技术生成

算法原理:
用单调递增栈,当该元素可以入栈的时候,栈顶元素就是它左侧第一个比它小的元素。
以:3 4 2 7 5 为例,过程如下:(小到大)

AcWing 830. 单调栈 - AcWing

#include<iostream>
using namespace std;
const int N=1e6+5;
int stk[N],tt;

int main() {
	int n;
	cin>>n;
	while(n--) {
		int x;
		scanf("%d",&x);
		while(tt&&stk[tt]>=x)tt--;//如果栈顶元素大于等于当前待插入的值,就出栈
		if(!tt) {
			//说明左边没有比他大的
			cout<<-1<<" ";
		} else {
			cout<<stk[tt]<<" ";
		}
		stk[++tt]=x;
	}
	return 0;
}

 

 154. 滑动窗口 - AcWing题库

解题思路(以最大值为例):

由于我们需要求出的是滑动窗口的最大值。

如果当前的滑动窗口中有两个下标 i 和 j ,其中i在j的左侧(i<j),并且i对应的元素不大于j对应的元素(nums[i]≤nums[j]),则:

当滑动窗口向右移动时,只要 i 还在窗口中,那么 j 一定也还在窗口中。这是由于 i 在 j 的左侧所保证的。

因此,由于 nums[j] 的存在,nums[i] 一定不会是滑动窗口中的最大值了,我们可以将nums[i]永久地移除。

因此我们可以使用一个队列存储所有还没有被移除的下标。在队列中,这些下标按照从小到大的顺序被存储,并且它们在数组nums中对应的值是严格单调递减的。

当滑动窗口向右移动时,我们需要把一个新的元素放入队列中。

为了保持队列的性质,我们会不断地将新的元素与队尾的元素相比较,如果新元素大于等于队尾元素,那么队尾的元素就可以被永久地移除,我们将其弹出队列。我们需要不断地进行此项操作,直到队列为空或者新的元素小于队尾的元素。

由于队列中下标对应的元素是严格单调递减的,因此此时队首下标对应的元素就是滑动窗口中的最大值。

窗口向右移动的时候。因此我们还需要不断从队首弹出元素保证队列中的所有元素都是窗口中的,因此当队头元素在窗口的左边的时候,弹出队头。

#include<iostream>
#include<deque>
#include<algorithm>
using namespace std;
const int N=1e6+5;
int a[N],q[N],hh,tt=-1;

int main() {
	int n,k;
	cin>>n>>k;
	for(int i=1; i<=n; i++) {
		scanf("%d",&a[i]);
	}
	deque<int>q;
	for(int i=1; i<=n; i++) {
		while(q.size()&&q.back()>a[i]) {
			//新进入窗口的值小于队尾元素
			q.pop_back();
		}
		q.push_back(a[i]);//将新元素入队
		if(i-k>=1&&q.front()==a[i-k]) {
			//判断队头是否滑出了窗口
			//队头出列
			q.pop_front();
		}
		if(i>=k)//窗口i形成了,输出队头对应的值
			cout<<q.front()<<" ";
	}
	q.clear();//清空队列
	cout<<endl;
	for(int i = 1; i <= n; i++) {
		while(q.size() && q.back() < a[i]) q.pop_back();//新进入的元素大于队尾元素
		q.push_back(a[i]);
		if(i - k >= 1 && a[i - k] == q.front())
			q.pop_front();//判断队头是否出列
		if(i >= k) cout << q.front() << " ";
	}
	return 0;
}

在Python中,单调栈和单调队列是两种不同的数据结构。单调栈是一个栈,它的特点是栈内的元素是单调的,可以是递增或递减的。在构建单调栈时,元素的插入和弹出都是在栈的一端进行的。与此类似,单调队列也是一个队列,它的特点是队列内的元素是单调的,可以是递增或递减的。在构建单调队列时,元素的插入是在队列的一端进行的,而弹出则是选择队列头进行的。 单调队列在解决某些问题时,能够提升效率。例如,滑动窗口最大值问题可以通过使用单调队列来解决。单调队列的结构可以通过以下代码来实现: ```python class MQueue: def __init__(self): self.queue = [] def push(self, value): while self.queue and self.queue[-1 < value: self.queue.pop(-1) self.queue.append(value) def pop(self): if self.queue: return self.queue.pop(0) ``` 上述代码定义了一个名为MQueue的类,它包含一个列表作为队列的存储结构。该类有两个方法,push和pop。push方法用于向队列中插入元素,它会删除队列尾部小于插入元素的所有元素,并将插入元素添加到队列尾部。pop方法用于弹出队列的头部元素。 总结来说,单调栈和单调队列都是为了解决特定问题而设计的数据结构。单调栈在构建时元素的插入和弹出都是在栈的一端进行的,而单调队列则是在队列的一端进行的。在Python中,可以通过自定义类来实现单调队列的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值