数据结构:单调栈、单调队列

迈向光明之路,注定荆棘丛生

一、单调队列:

(一)概念及作用:

维护区间的最大值(最小值),单调队列本质上是一个单调递减(单调递增)序列。

(二)维护演示:

在这里插入图片描述
下面,在一个数组中以区间为三,我们维护一个单调递减的单调队列。
最小值为23,即队首元素
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(三)代码演示:

int main() {
	int n, k;
	scanf("%d%d", &n, &k);
	vector<int> arr;
	deque<int> q;
	for (int i = 0, a; i < n; i++)
	{
		scanf("%d", &a);
		arr.push_back(a);
	}
	output(arr);
	for (int i = 0; i < n; i++) {
		while (!empty(q) && arr[q.back()] > arr[i]) {
			q.pop_back();
		}
		q.push_back(i);
		if (i - q.front() == k) q.pop_front();
		printf("[%d, %d] : min = %d\n", max(i - k + 1,  0), i, arr[q.front()]);
	}
	return 0;
}

(四)例题剖析:

在这里插入图片描述

剖析:

首先求区间和,我们需要构建前缀和数组,依次遍历前缀和数组元素的每个值,以M为3举例,维护一个长度为3的单调队列。
例如,当遍历到数组下标6时,用当前元素120减去矩形区域的最小值就得到当前元素的最大和。遍历完整个数组,得到最大值。
在这里插入图片描述

代码演示:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<vector>
#include<queue>
using namespace std;
int main() {
	int n, m;
	scanf("%d%d", &n, &m);
	vector<int> v( n + 1);
	deque<int> q;
	v.push_back(0);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &v[i]);
		v[i] += v[i - 1];
	}
	int ans = 0;
	q.push_front(0);
	for (int i = 1; i <= n; i++) {
		ans = max(ans, v[i] - v[q.front()]);
		while (!q.empty() && v[q.back()] > v[i]) q.pop_back();
		q.push_back(i);
		if (i - q.front() == m)  q.pop_front();
	}
	printf("%d", ans);
	return 0;
}

二、单调栈:

(一)概念及作用:

维护距离当前元素最近的最大值(最小值),单调队列本质上是一个单调递减(单调递增)序列。

(二)维护演示:

相比于单调队列,单调栈在维护时不需要维护头部出元素,相对比较简洁。
在这里插入图片描述
在这里插入图片描述

(三)代码演示:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stack>
#include<vector>
using namespace std;
int main() {
	int n, k;
	scanf("%d%d", &n, &k);
	vector<int> arr;
	stack<int> s;
	vector<int> lmin(n + 1), rmin(n + 1);
	arr.push_back(-1);
	for (int i = 0, a; i < n; i++)
	{
		scanf("%d", &a);
		arr.push_back(a);
	}
	arr.push_back(-1);
	for (int i = 0; i < n; i++) {
		while (!s.empty() && arr[s.top()] > arr[i]) {
			rmin[s.top()] = i; 
			s.pop();
		}
		s.push(i);
	}
	while (!s.empty()) s.pop();
	for (int i = n - 1; i >= 0; i--) {
		while (!s.empty() && arr[s.top()] > arr[i]) {
			lmin[s.top()] = i;
			s.pop();
		}
		s.push(i);
	}
	printf("   ");
	for (auto x : lmin) printf("%3d", x); printf("\n");
	for (auto x : rmin) printf("%3d", x); printf("\n");
	for (int i = 1; i <= n; i++) {
		printf("arr[%d] = [%d, %d]\n", i, arr[lmin[i]], arr[rmin[i]]);
	}
	return 0;
}

(四)例题剖析:

在这里插入图片描述

代码演示:

class Solution {
public:
    int trap(vector<int>& height) {
        int ans = 0, n = height.size();
        stack<int> s;
        for(int i = 0; i < n; i++){
            while(!s.empty() && height[s.top()] <= height[i]){
                int height_pre = height[s.top()];
                s.pop();
                if(s.empty()) break;
                ans += (min(height[i], height[s.top()]) - height_pre) * (i - s.top() - 1);
            }
            s.push(i);
        }
        return ans;
    }
};

三、结束语:

单调队列和单调栈是一种求最值的高效的数据结构,其中形式上类似单调函数。接下来小编会继续更新数据结构的知识,希望和大家一起学习,大家支持一下吧!

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值