单调栈

3 篇文章 0 订阅
3 篇文章 0 订阅

摘自https://blog.csdn.net/wubaizhe/article/details/70136174

1、定义

单调栈是一种特殊的栈,其栈内的元素都保持一个单调性(单调递增或者递减)。

  • 单调递增栈,从栈底到栈顶依次递增(单调非递减栈:允许有相等)
  • 单调递减栈,从栈底到栈顶依次递减(单调非递增栈:允许有相等)

假设下图是一个栈内元素的排列情况(单调递增的栈):
这里写图片描述

2、作用

利用单调栈,可以找到从左(或者右)遍历第一个比它小(或者大)的元素的位置。

3、算法过程

假设有一个单调递增的栈 S和一组数列: a = { 5 3 7 4}
数组L[i]表示 第i个数向左遍历的第一个比它小的元素的位置

如何求L[i]?

3.1 朴素的算法 O(n^2)

可以按顺序枚举每一个数,然后再依此向左遍历。 但是当数列单调递减时,复杂度是严格的O(n^2)。

3.2 单调栈 O(n)

我们按顺序遍历数组(i : 1 -> n),然后构造一个单调递增栈。栈中存放的是元素下标,而非元素本身。

        {5 3 7 4}
(1)i = 1时,因为栈为空,L[1] = 0,此时再将第一个元素的位置下标1存入栈中。
此时栈中情况:

                            这里写图片描述

 

(2)i = 2时,因当前元素a[i] = 3小于栈顶元素下标1对应的元素a[1] = 5,故将下标1弹出栈, 此时栈为空 ,故L[2] = 0 。然后将元素3对应的位置下标2存入栈中。
此时栈中情况:

                            这里写图片描述

(3)i = 3时,因当前元素a[i] = 7大于栈顶元素下标2对应的元素a[2] = 3,故
L[3] = S.top() = 2 (栈顶元素的值,说明第一个比它小的元素的下标为多少),然后将元素7对应的下标3存入栈 。
此时栈中情况:

                           这里写图片描述

(4)i = 4时,因当前元素a[i] =4小于栈顶元素下标3对应的元素a[3] = 7,为保持单调递增的性质,应将栈顶元素下标3弹出 ,而当前元素a[i] =4大于弹出元素后的栈顶元素下标2对应的元素a[2] = 3,不需要再继续弹出, 此时 L[4] = S.top() = 2;然后将元素4对应的下标4存入栈。
此时栈中情况:

                                    这里写图片描述

(5)至此 算法结束

对应的结果:
a : 5 3 7 4
L : 0 0 2 2

(6)总结
一个元素向左遍历的第一个比它小的数的位置就是将它插入单调栈时栈顶元素的值,若栈为空,则说明不存在这么一个数。然后将此元素的下标存入栈,就能类似迭代般地求解后面的元素

4、模板

stack<int> s;
for(int i = 1; i <= n; ++i)
{
    while(s.size() && a[s.top()] >= a[i]) s.pop();
    if(s.empty()) l[i] = 0;
    else l[i] = s.top();
    s.push(i);
}


5、应用

1.给定一组数,针对每个数,寻找它和它左边第一个比它小的数之间有多少个数。

2.给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列的长度最大。

3.给定一序列,寻找某一子序列,使得子序列中的最小值乘以子序列所有元素和最大。

 

例题:http://poj.org/problem?id=3250

大概意思是向右遍历找第一个大于i的数,可以直接套模板了((

#include <iostream>
#include <algorithm>
#include <stack>
#define ll long long
int a[100010]={0};
int r[100010]={0};
using namespace std;
int main()
{
	ios::sync_with_stdio(false);
	
	int n,i;
	
	cin>>n;
	
	for(i=1;i<=n;i++) cin>>a[i];
	
	stack <int> sk;
	
	for(i=n;i>=1;i--){
		
		while(sk.size()&&a[sk.top()]<a[i]) sk.pop();
		
		if(sk.empty()) r[i]=n+1;
		
		else r[i]=sk.top();
		
		sk.push(i);
		
	}
	
	ll sum=0;
	
	for(i=1;i<=n;i++){
		
		sum+=r[i]-i-1;
		
	}
	
	cout<<sum<<endl;
	
	return 0;
	
 } 


 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
单调栈是一种常用的数据结构,用于解决一类特定的问题,其中最常见的问题是找到数组中每个元素的下一个更大或更小的元素。在Codeforces编程竞赛中,单调栈经常被用于解决一些与数组相关的问题。 下面是单调栈的一般思路: 1. 创建一个空栈。 2. 从左到右遍历数组元素。 3. 对于每个元素,将其与栈顶元素进行比较。 - 如果当前元素小于等于栈顶元素,则将当前元素入栈。 - 如果当前元素大于栈顶元素,则将栈顶元素弹出,并将当前元素入栈。 4. 重复步骤3,直到遍历完所有元素。 这样,最后剩下的栈中元素就是没有下一个更大或更小元素的元素。在使用单调栈求解具体问题时,我们可以根据需要进行一些特定的操作。 例如,如果要找到一个数组中每个元素的下一个更大的元素,可以使用单调递减栈。具体操作如下: 1. 创建一个空栈和一个空结果数组。 2. 从左到右遍历数组元素。 3. 对于每个元素,将其与栈顶元素进行比较。 - 如果当前元素小于等于栈顶元素,则将当前元素入栈。 - 如果当前元素大于栈顶元素,则将栈顶元素弹出,并将其在结果数组中的位置记录为当前元素的下一个更大元素的索引。 4. 将当前元素入栈。 5. 重复步骤3和4,直到遍历完所有元素。 6. 结果数组中没有下一个更大元素的位置,可以设置为-1。 以下是一个使用单调递减栈求解下一个更大元素问题的示例代码: ```cpp #include <iostream> #include <stack> #include <vector> std::vector<int> nextGreaterElement(std::vector<int>& nums) { int n = nums.size(); std::vector<int> result(n, -1); std::stack<int> stack; for (int i = 0; i < n; i++) { while (!stack.empty() && nums[i] > nums[stack.top()]) { result[stack.top()] = i; stack.pop(); } stack.push(i); } return result; } int main() { std::vector<int> nums = {1,3, 2, 4, 5, 1}; std::vector<int> result = nextGreaterElement(nums); for (int i = 0; i < result.size(); i++) { std::cout << "Next greater element for " << nums[i] << ": "; if (result[i] != -1) { std::cout << nums[result[i]]; } else { std::cout << "None"; } std::cout << std::endl; } return 0; } ``` 以上代码将输出: ``` Next greater element for 1: 3 Next greater element for 3: 4 Next greater element for 2: 4 Next greater element for 4: 5 Next greater element for 5: None Next greater element for 1: None ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值