C++之单调栈

单调栈的性质

单调栈是一种特殊的栈,特殊之处在于栈内的元素都保持一个单调性。
假设下图是一个栈内元素的排列情况(单调递增的栈):
引用
此时插入情况有两种:
(1)插入元素大于栈顶元素:
因为7 > 6,满足栈内元素单调递增的性质,所以可以直接插入7到栈顶
引用
(2)当插入元素小于栈顶元素的时候,当插入3的时候,需要将栈顶的6,4弹出,在插入,例如:
引用

功能

利用单调栈可以找出从左/右遍历第一个比它小/大的元素的位置
例如说:
现在有一个数组a[4] = {5,7,3,4},要你找出每一个元素左边最靠近它的最小元素的位置(下标+1),如果没有元素比它小,则输出0
此时我们会想到,从每一个数字开始想左边遍历,这是一个朴素的算法,我们就不再多说了。
我们要说的是怎么样利用单调栈来解决该问题
1.设计一个数组res[4],来保存结果
2.开一个栈,这个栈的性质是,当栈为空的时候,将对应的res[i] = 0;当栈顶元素是从左开始,输入的数中最小的那个的下标,如果进栈的元素小于或者等于a[栈顶元素],则输出0;如果大于,则输出栈顶元素+1。

下面上代码:

#include<iostream>
#include<stack>
using namespace std;

int main() {
	//n表示要找的总数
	int n; cin >> n;
	//a用来保存输入的数据
	int* a = new int[n];
	//res_left用来保存对应的结果
	int* res_left = new int[n];
	stack<int>s;
	for (int i = 0; i < n; i++)cin >> a[i];
	//找出a[i]左边和右边最近的一个比他小的值的下标
	for (int i = 0; i < n; i++) {
		while (!s.empty() && a[i] <= a[s.top()])s.pop();
		if (s.empty())res_left[i] = 0;
		else res_left[i] = s.top() + 1;
		s.push(i);
	}
	//这个栈就是递减的一个,取最小值的时候,复杂度是O(1)
	for (int i = 0; i < n; i++) {
		cout << res_left[i] << " ";
	}
}
输入数据: 6
4 7 8 5 3 1
输出数据:
0 1 2 1 0 0

当然这是一个简单的小应用

例题1:

题目背景:
高个子同学可以看到身高比自己矮的同学,但是目光一旦遇到高于或等于自己的同学后,便无法继续向前看。先给出所有同学的身高,求所有同学能看到的同学数量之和。
可能不和逻辑…
输入格式:
第一行一个n,表示总共有多少名同学
接下来输入n个数字,表示从后到前的同学的身高
输出格式
一个数字,同学们能看到的同学的总数

解题思路:
1.朴素的算法:先读入数据,然后从最后一名开始向前遍历,遇到小于自己身高的就加1,遇到大于等于自己身高的就停止,时间复杂度应该是O(n*logn)
2.利用单调栈:每读入一个数据,就判断前面有多少名同学能看到他,个子高的能看到个子矮的,所以这应该是递减的栈,时间复杂度是O(n)

代码实现:

#include<iostream>
#include<stack>
using namespace std;

int main() {
	int n; cin >> n;
	int sum = 0;
	//开一个栈,单调递减,对于栈顶元素而言,能看到他的就是后面元素的个数
	stack<int>s;
	for (int i = 0; i < n; i++) {
		int temp;
		cin >> temp;
		//当输入的人较高时,将较矮的人剔除栈
		while (!s.empty() && temp >= s.top())s.pop();
		sum += s.size();
		s.push(temp);
	}
	cout << sum << endl;
	return 0;
}
输入数据:6
188 169 180 172 190 165
输出数据:
5

例题2:

输入格式:
一个整数n,表示总共有n个宽度为1的矩形块
n个整数,表示每一个矩形块的高度,按照输入的顺序,排列相应高度的矩形块
输出格式:
一个整数,表示这些矩形块能够构成的最大矩形块。
引用
仔细观察可知,这个矩形一定以某一个输入的矩阵高度为高度
解题思路:
1:常规思路,将这些高度保存起来,用一个数组;然后对数组进行遍历,对每一个数字进行的操作是,向左右找,直到碰到高度小于自己高度或者是到边界的时候,算一下总共的矩阵块数,再乘以高度。这样每一个输入的矩阵都对应一个以他为高度形成的最大矩阵,然后取最大值就行。但是这个算法的最坏情况是O(N*2)显然是一个很大的数据,这个属于朴素算法

2.利用单调栈:这里就不多bb,直接上代码,代码上面有注释

#include<iostream>
#include<stack>
using namespace std;

//这个节点用来保存该矩阵的高度,能触及的最边缘的下标
class Node {
public:
	int val, left, right;
};
//这是一个根据矩阵高度排列的递增栈
stack<Node>s;
//为什么要设计一个递增的栈:因为遇到高度小的就需要停止扩展边界
int main() {
	int n; cin >> n;
	Node* node = new Node[n + 5];
	//初始化
	for (int i = 0; i < n; i++) {
		cin >> node[i].val;
		node[i].left = node[i].right = i;
	}

	long long int area = 0;

	//为了当栈内还剩余元素的时候,不需要单开逻辑判断
	//简单的说就是为了清空栈内元素
	node[n + 1].val = -1;
	node[n + 1].left = node[n + 1].right = n + 1;

	for (int i = 0; i < n + 1; i++) {
		//由于这是一个递增的栈,当要压入的元素小于栈顶元素的时候
		while (!s.empty() && node[i].val <= s.top().val){
			Node newnode = s.top();
			s.pop();
			//说明将要压入的矩阵可以向左边拓展
			node[i].left = newnode.left;
			//如果删除栈顶元素之后还有元素,那么栈顶元素可以向右拓展
			if (!s.empty()) {
				s.top().right = newnode.right;
			}
			//弹出每一个矩阵的时候,计算该矩阵能形成的最大矩阵的面积
			long long int val = ((long long)newnode.right - newnode.left + 1) * (long long)newnode.val;
			if (val > area)area = val;
		}
		s.push(node[i]);
	}
	cout << area << endl;
}
输入数据:6
2 1 5 6 2 3
输出数据:
10
  • 13
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
单调栈是一种特殊的栈结构,它可以快速找到栈中的最大值或最小值。单调栈常用于解决一些数组、字符串中的问题,例如求下一个更大的元素、求前一个更大的元素等等。 下面是单调栈的代码模板: ``` stack<int> mono_stack; for (int i = 0; i < n; i++) { while (!mono_stack.empty() && mono_stack.top() > x[i]) { mono_stack.pop(); } mono_stack.push(x[i]); } ``` 其中,`x` 是一个数组,`n` 是数组的长度,`mono_stack` 是单调栈。这段代码的意思是:遍历数组 `x`,对于每个元素 `x[i]`,将其压入栈中,然后不断弹出栈顶元素,直到栈为空或者栈顶元素不大于 `x[i]`。这样,最终单调栈中的元素就是一个单调递增的序列。 如果要求下一个更大的元素,可以在遍历数组时,记录每个元素的下标,然后在弹出栈顶元素时,更新下标对应的答案。代码如下: ``` stack<pair<int, int>> mono_stack; for (int i = 0; i < n; i++) { while (!mono_stack.empty() && mono_stack.top().first < x[i]) { ans[mono_stack.top().second] = x[i]; mono_stack.pop(); } mono_stack.push(make_pair(x[i], i)); } ``` 其中,`ans` 是答案数组,`ans[i]` 表示第 `i` 个元素的下一个更大的元素。在弹出栈顶元素时,更新对应下标的答案即可。 如果要求前一个更大的元素,可以将数组逆序遍历,然后按照同样的方式求解。代码如下: ``` stack<pair<int, int>> mono_stack; for (int i = n - 1; i >= 0; i--) { while (!mono_stack.empty() && mono_stack.top().first < x[i]) { ans[mono_stack.top().second] = x[i]; mono_stack.pop(); } mono_stack.push(make_pair(x[i], i)); } ``` 以上就是单调栈的代码模板,可以根据具体问题进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值