1.单调栈

单调栈:一种数据结构。

  1. 定义:栈中存在的元素是单调递增单调递减的。

  2. 本质:空间换时间。
    2.1 单调递增栈:栈中的元素 --从栈底到栈顶是递增的。(也可反过来定义)
    2.2 单调递减栈:栈中的元素 --从栈底到栈顶是递减的。

  3. 单调栈的作用:存放之前遍历过的元素,用来和当前的元素进行对比。

  4. 使用单调栈的目的:寻找数组中每个元素的左边或右边第一个更大或更小元素

  5. 三个判断条件。
    (1)当前遍历的元素T[i]小于栈顶元素T[st.top()]的情况
    (2)当前遍历的元素T[i]等于栈顶元素T[st.top()]的情况
    (3)当前遍历的元素T[i]大于栈顶元素T[st.top()]的情况


下面通过题目来进行理解:

一、力扣1475. 商品折扣后的最终价格

给你一个数组 prices ,其中 prices[i] 是商店里第 i 件商品的价格。
商店里正在进行促销活动,如果你要买第 i 件商品,那么你可以得到与 prices[j] 相等的折扣,其中 j 是满足 j > i 且 prices[j] <= prices[i] 的 最小下标 ,如果没有满足条件的 j ,你将没有任何折扣。
请你返回一个数组,数组中第 i 个元素是折扣后你购买商品 i 最终需要支付的价格。

非常经典的一道单调栈的题,题目的要求就是求数组中每个元素右边的第一个较小元素,符合单调栈的定义,可以用单调栈来解决,当然,直接用二重循环也可以,但是要耗费更多的时间。

第一步----定义栈和辅助数组:
stack<int>stk;
vector<int>ans(prices.begin(),prices.end());//define the answer array

这里用C++里的STL来定义一个栈,放int类型的数据。
定义一个ans数组,用来存储结果。

其中,栈里放元素下标,以便可以在后续更新值的时候进行对应。
ans数组直接用prices数组初始化,方便后续值不变时直接保留,省去后续的遍历赋值操作。

第二步----遍历数组,进行单调栈操作
for(int i=0;i<n;i++){
	if(!stk.empty() && prices[i]>prices[stk.top()]) 	stk.push(i);
	else{
		while(!stk.empty() && prices[i]<=prices[stk.top()]){
 			ans[stk.top()]=prices[stk.top()]-prices[i];//:=
 			stk.pop();
		}
			stk.push(i);
	}
}

本题求右边第一个小于对于它的元素,所以只能分到两种情况:
(1)prices[i]<=prices[st.top()]:

  • 此时我们找到了符合条件的元素,应当进行ans数组的更新(&出栈操作),然后循环判断是否还有符合条件的元素。所以上面用了while循环,注意细节!

(2)prices[i]>prices[st.top()]:

  • 此时为一般情况,直接进行入栈操作

当然,我们发现无论是否找到了合适的元素,在每个方面判断结束后都要进行入栈操作,所有可以将入栈操作合到一起,即:

for(int i=0;i<n;i++){
  	while(!stk.empty() && prices[i]<=prices[stk.top()]){
  		ans[stk.top()]=prices[stk.top()]-prices[i];//:=
  		stk.pop();
	}
	stk.push(i);//small ,equal,and big
}

ok,单调栈就是这样,是的,你没看错,就是这么简单! 完整的代码如下:

class Solution {
public:
    vector<int> finalPrices(vector<int>& prices) {
    	int n=prices.size();
    	
    	stack<int>stk;
    	vector<int>ans(prices.begin(),prices.end());//define the answer array
    	
    	for(int i=0;i<n;i++){
    		while(!stk.empty() && prices[i]<=prices[stk.top()]){
    			ans[stk.top()]=prices[stk.top()]-prices[i];//:=
    			stk.pop();
			}
			stk.push(i);//small ,equal,and big
		}
		return ans;

    }
};


力扣739. 每日温度

给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。

这道题也一样,只不过是找右边第一个更大元素。

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        int n=temperatures.size();
        stack<int>stk;
        vector<int>ans(n,0);//巧妙的初始化方式 
        for(int i=0;i<n;i++){//扫描数组的元素      	
        	while(!stk.empty() && temperatures[i]>temperatures[stk.top()]){//遍历栈 
        		ans[stk.top()]=i-stk.top();
        		stk.pop();
			}		
				stk.push(i);
		}
	
		return ans;
    }
};

初始化的方式可以根据具体的题目要求来看,做多了就熟练了。

至于栈定义好后要不要先加入第一个元素,完全根据个人喜好了,都可以。

刚开始写时最好将三种情况分开写,更容易理解。如:

for (int i = 1; i < T.size(); i++) {
       if (T[i] < T[st.top()]) {                       // 情况一
           st.push(i);
       } else if (T[i] == T[st.top()]) {               // 情况二
           st.push(i);
       } else {
           while (!st.empty() && T[i] > T[st.top()]) { // 情况三
               result[st.top()] = i - st.top();
               st.pop();
           }
           st.push(i);
       }
   }

当然,类似的题目还有很多,比如接雨水问题等等,但基本的套路就是这样了,万变不离其宗,只要抓住本质,一切都可迎刃而解!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

追逐远方的梦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值