算法之单调栈总结

前序:来自b站左神,个人整理为笔记,如有错误,感谢指出!、

由于栈内元素始终保持单调递增,因此这种数据结构也叫做单调栈

运用:// 单调栈求每个位置左右两侧,离当前位置最近、且值严格小于的位置(大于)
// 给定一个可能含有重复值的数组 arr
// 找到每一个 i 位置左边和右边离 i 位置最近且值比 arr[i] 小的位置
// 返回所有位置相应的信息。

难点:遇到相等的时候如何处理

题目:不知道为啥只能通过80%。快读优化后了差1ms,emmm真棒。

基本模板:

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
const int N = 1e6 + 10;
int arr[N];
int help[N];//数组模拟栈,记录位置下标 
int ans[N][2];//二维数组记录答案
int n , r;//r作为入栈记录 

void solve()
{
	r = 0;
	int index;//当前位置下标
	//遍历 
	for(int i = 0 ; i < n ; i++)
	{
	   //通过位置下标就可以获得数组的值,所以记录下标即可 
	   while(r > 0 && arr[i] <= arr[help[r - 1]])//当破坏大压小的原则的时候 前提是栈内还有数 通过r的计数可以判断 
	   {
	      	index = help[--r];//开始找到此时下标左边最近且小于的数,特判一下是否栈为空 
			ans[index][0] = r > 0 ? help[r - 1]:-1;//如果栈为空index下标就是栈最后一个数,那就没有最左了记为-1;边界的处理 
			ans[index][1] = i;//当前数再进入  
	   }
	   //没有破坏正常入栈 
	   help[r++] = i;//记录位置	
	}
	
	//清算阶段,最后栈内剩下的数,已经不存在最右且小的了 
	while(r > 0)
	{
	   index = help[--r];
	   ans[index][0] = r > 0 ? help[r - 1] : -1;
	   ans[index][1] = -1;	
	}
	
	//修正阶段,遇到等于的时候的处理 ,从右往左,n - 1位置是最后一个右侧答案一定是-1 ,处理右边界 
	for(int i = n - 2 ; i >= 0 ; i--)
	{
	   if(ans[i][1] != -1 && arr[ans[i][1]] == arr[i])//先前已经保存的是相等的数 
	   {
	      	ans[i][1] = ans[ans[i][1]][1];//把相等数存的正确答案也是此时的答案 
	   }	
	} 
}

int main()
{
    cin >> n;
    for(int i = 0 ; i < n ; i++)
	cin >> arr[i];
	solve();
	for(int i = 0 ; i < n ; i++)
	cout<<ans[i][0] << " " << ans[i][1] << endl;
	return 0;
	
}

题目二:思路:前面提到的下一个,而且只要考虑右边,等于的时候直接也入栈不影响

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
const int N = 1e6 + 10;
int hot[N];
int help[N];//数组模拟栈,记录位置下标 
int ans[N];//二维数组记录答案
int n , r;//r作为入栈记录 

int main()
{
	cin >> n;
	for(int i = 0 ; i < n ; i ++)
	{
		cin >> hot[i];
	}
	
	int index;
	for(int i =0 ; i < n ; i++)
	{
		//小压大被破坏 
		while(r > 0 &&  hot[help[r - 1]] < hot[i]){
			index = help[--r];
			ans[index] = i - index; 
		}
		help[r++] = i;
	}
	
	for(int i = 0 ; i < n ; i++)
	cout << ans[i] << " ";
	return 0;
}

题目三:

分析 假设有组数组3 4 5 2,单调栈进行维护当被破坏的时候依次进行枚举,因为5是栈顶元素,所以此时只有一种情况有最小值那就是5本身,继续判断循环再次进入4我们知道因为是单调的此时i到cur就知道结尾的情况,开头只有本身开头,1 * 2 * 4 = 8.emm大概就这意思。

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;
const int mod = 1000000007;
const int N = 3e5 + 10;
int help[N];
int arr[N];
int n,r;

int solve()
{
    long long int ans = 0;
    r = 0;
    
    for(int i = 0 ; i < n ; i++)
    {
    	
    	while(r > 0 && arr[help[r - 1]] >= arr[i])//当栈内元素大于进栈元素,破坏了大压小() 
		{
			
		   int cur = help[--r];
		   int left =  r == 0 ? -1 : help[r - 1];
		   ans = (ans + (long)(cur - left) * (i - cur) * arr[cur]) % mod;
		} 
		help[r++] = i;
	}
	
	while(r > 0)
	{
		int cur = help[--r];
		int left = r == 0 ? -1 : help[r - 1];
		ans = (ans + (long)(cur - left) * (n - cur) * arr[cur]) % mod;
	}
	return (int)ans;
}

int main()
{
	cin >> n;
	for(int i = 0; i < n ; i++)
	cin >> arr[i];
	int res = solve();
    cout << res << endl; 
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值