刷题(10)-单调栈和单调队列

单调栈

用途:O(n)时间,求一个数组的每个元素i,它的左边最近或者右边最近比它小或者大(or大于等于or小于等于)的元素。

  1. 最近比它的,维护一个严格单调递减的栈(栈底到栈顶,是从大到小),while的时候栈顶元素小于等于当前元素,我们pop。(假如是大于等于,那就是单调递减的栈,小于的时候pop)
  2. 最近比它的,维护一个严格单调递增的栈(栈底到栈顶,是从小到大),while的时候栈顶元素大于等于当前元素,我们pop。(小于等于,同上)

求左边那就是从左到右遍历一遍,从右边就是从右到左遍历一遍。

求左右两边第一个比它小的模板

void fun(vector<int> vec){
	int n=vec.size();

    // left[i] 表示的是在vec数组中 左边最近的比vec[i]小的下标, 假如左边没有最小的存-1
    // right[i]  表示的是在vec数组中 右边最近的比vec[i]小的下标, 假如右边没有最小的存n(vec数组长度)
	vector<int> left(n),right(n); 

	//求左右两边最近小的,所以维持从小到大
	stack<int> stk;
	
	// 求左边
	for(int i=0;i<n;++i){
        
        // 栈不为空 && 栈顶元素大于当前元素
        // 也就破坏了单调递增性, 所以需要出栈
        // 假设现在 栈里是 4 6, 要入栈的是5, 因为5比6小, 5以后的元素,最近的肯定轮不到6,有5在,所以6需要出栈。
		while(stk.size() && vec[stk.top()]>= vec[i]) 
			stk.pop();
		
		if(stk.empty())
			left[i]=-1;  // 假如左边没有最小的存-1
		else
			left[i]=stk.top();
		stk.push(i);
	}
	
	
	while(stk.size())
		stk.pop();
	
	//求右边
	for(int i=n-1;i>=0;--i){
		while(stk.size() && vec[stk.top()]>=vec[i])
			stk.pop();
		if(stk.empty())
			right[i]=n;
		else
			right[i]=stk.top();
		stk.push(i);
	}
	
	
	do something
	
} 





//另一种实现,我觉得不好  不要看!!
void fun(vector<int> vec){
	
	stack<int> inc; //求左右两边第一个小的,用递增栈,栈中存的是下标
	
	// 遍历数组
	for(int i=0;i<vec.size();++i){
		
		//栈非空,且当前遍历到的元素的值 小于等于 栈顶元素代表的值(破坏了递增性) 
		while(inc.size() && vec[i]<= vec[inc.top()]){
			
			//当前遍历到的元素的值小于栈顶元素代表的值的逻辑
			if(vec[i]<vec[inc.top]){
			// 弹出栈顶元素index。对这个元素进行处理
			//假如栈不空,那么index左边第一个比它对应值小的下标就是栈顶,否则-1(null)
			//index右边第一个比它对应值大的下标就是i
			int index=inc.top();
			inc.pop();
			int left= inc.empty()?-1:inc.top();
			int right =i;
			/*
			do something
			*/
			}
			
			// 当前遍历到的元素的值等于栈顶元素代表的值的逻辑
			else {  
				do something
                //可能是插入到栈顶元素中去(这时候stack存的是vector<int>)
			}
			
		}
		
		inc.push(i);
		
	}
	
	// 数组虽然遍历完了,但是栈里可能还有元素
	/* 这时候对于栈里的每个元素,它们右边第一个比它小的元素都是右边界heights.size()或者说没有(null),而左边第一个比每个元素小的元素,跟上面的逻辑一样,依旧是栈里的下一个元素,or 栈空的话,是左边界(-1 或者说是没有)
	*/
	int end=heights.size();
	while(inc.size()){
		int index=inc.top();
		inc.pop();
		int left= inc.empty()?-1:inc.top();
		int right =end;
		do something;
	}
	
}

经典例题

leetcode 84. 柱状图中最大的矩形  hard (重要)_speargod的博客-CSDN博客

leetcode 42. 接雨水  hard_speargod的博客-CSDN博客

​ leetcode 739. 每日温度 medium ​_speargod的博客-CSDN博客

​ leetcode 503. 下一个更大元素 II medium ​_speargod的博客-CSDN博客

单调队列

应用:滑动窗口的最值(最大最小问题)

经典例题: leetcode 239. 滑动窗口最大值  hard(重要)_speargod的博客-CSDN博客

思想: 假设现在求滑动窗口的最大值问题, 我队列现在最后一个元素是6, 现在9要进来,那现在最大值肯定轮不到6了,因为6会比9先出去,并且9比6大。所以我们维护一个单调递减的队列即可。

总结:

不管是单调栈还是单调队列:

的,那栈or队列就需要单调递, 当栈顶or队列尾(小于等于 or 小于)当前元素,那就出栈(队),

求小的,栈or队列就需要单调递增, 当栈顶or队列尾(大于等于or 大于)当前元素,那就出栈(队)

同时栈和队列存的是下标

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值