【算法】基础数据结构

一、单调栈

1. 概念

满足单调性的栈结构,常用于 RMQ 问题。

2. 实现

为满足单调性,我们在栈的基础上额外判断以下栈顶元素是否大于/小于当前元素。以下面的序列 \(1\;7\;4\;3\;2\;8\) 为例,需要求每一个数右边第一个比它大的数。考虑维护单调递减栈,才能保证不会有数没有找到答案便被弹出栈了。

现在将 \(7\) 放入已有 \(1\) 的栈中。

image

由于这是一个单调递减栈,\(7 > 1\),此时将 \(1\) 弹出,放入 \(7\),并得出 \(1\) 右边第一个大于它的数就是 \(7\)。证明如下:

若当前栈顶为 \(x\),当前元素为 \(y\)。假设序列中存在一个数 \(z>x\)\(z\)\(x\)\(y\) 中间,为满足单调性,要么 \(z\) 还没入栈——这显然不可能,因为在 \(z\) 后面的元素 \(y\) 都已经入栈,要么 \(x\)\(z\) 弹出——那么 \(x\) 就一定不是当前栈顶。所以假设不成立。

然后一次将元素 \(4\;3\;2\) 放入,由于它们都符合单调递减性,所以无需弹出。

image

最后放入 \(8\) ,先和当前栈顶 \(2\) 比较。

image

发现 \(8>2\),不符合单调递减性,弹出 \(2\)。再将 \(8\) 和 当前栈顶 \(3\) 比较。

image

发现 \(8>3\),弹出 \(3\)。以此类推,最后 \(8\) 入栈时,栈里应该什么都没了。

3. 代码

/*
手写栈,stk[]是栈数组,top是栈顶
a[]是原数组,ansi[]存储答案
*/
for(int i=1;i<=n;i++){
   while(top!=0&&a[st[top]]<a[i]) ansi[st[top--]]=i;
   st[++top]=i;
}

时间复杂度显然 \(O(n)\)

二、单调队列

1. 概念

仿照单调栈的概念有,单调队列是具有单调性的队列。不同于寻常队列的是,这里用到的队列是双端对列。同样用于 RMQ 问题或滑动窗口问题。

2. 实现

为方便描述,这里默认单调队列为单调递减队列。

类似于单调栈。对于滑动窗口问题,由于队列的先进先出性,我们不仅要判断队尾的元素是否大于当前元素,还要判断队首元素是否超过窗口大小限制。

3. 代码

for(int i=1;i<=n;i++){
	while(qmin.empty()==false&&qmin.back().w>=a[i]) qmin.pop_back();
	while(qmin.empty()==false&&qmin.front().pos<i-k+1) qmin.pop_front();
	node now={i,a[i]};
	qmin.push_back(now);
	mini[i]=qmin.front().w;
}

同样,时间复杂度也同样 \(O(n)\)

4. 习题

emmmmm...挖个坑,保证八号之前绝对填完(吧)

posted @ 2023-07-03 21:28  Cloote  阅读(24)  评论(1编辑  收藏  举报