leetcode单调栈总结

本次主要总结利用单调栈解决leetcode中的第739题每日温度、第496题下一个更大的元素I、第503题下一个更大的元素II。

单调栈主要解决的几种问题:

  • 比当前元素更大的下一个元素
  • 比当前元素更大的前一个元素
  • 比当前元素更小的下一个元素
  • 比当前元素更小的前一个元素

需要思考的几个问题:

  • 如何操作单调栈
  • 严格单调栈还是非严格单调栈
  • 单调递增栈还是单调递减栈

先以739题为例看看如何操作单调栈,题目地址:739每日温度

github739题代码地址

如何操作单调栈

第739题的题意简单来说就是,给定一个数组nums,找到每个元素nums[i]的下一个比nums[i]大的元素对应的索引的差值,比如[73,74,75,71,69,72,76,73],输出[1,1,0,2,1,1,0,0]


结题思路:

最容易想到的结题思路是从前向后遍历,每次遍历一个元素,找到后面比它大的元素,但是很明显,有些元素我们需要重复遍历,增加了时间复杂度。比如找比75大的元素,我们需要遍历[71,69,72,76],找比71大的元素,我们需要遍历69,72,显然有重复遍历的区域。如果可以减少这部分的遍历次数,就可以提高整体的运行效率。
在这里插入图片描述
首先考虑初始状态,最后一个元素的输出肯定是0,因为最后一个元素找不到后面比它大的元素(ps.这个要看题意,比如503题重新定义了数组),我们考虑从后向前遍历,我们想象这样的场景,把数组想象成站成一队的人,这些人面对你站成一列,把数组中的元素想象成人的身高,如何求第2个人的下一个比他高的人呢?因为比第2个人矮的人我们都看不到,第一个露出来的就是答案
在这里插入图片描述
所以单调栈中存储一个从上向下递增的索引,每次遍历一个元素nums[i]的时候,

  1. 判断栈中是否有比nums[i]小的元素,如果有就出栈(因为本次操作之后,nums[i]会进栈,再向前遍历的时候,没有必要比较后面比nums[i]的元素了)
  2. 直到找到比nums[i]大的第一个元素,计算索引的差值
  3. nums[i]入栈,向前遍历,执行步骤1。

对于上面的例子,请看下图进行遍历的过程
简要介绍:第一行是正在遍历的数组,第二行是最后输出结果的数组,蓝色填充为遍历后确定的结果,绿色填充为栈中的元素(存储的原数组索引),粉色填充表示第一个比当前元素大的索引,紫色填充表示出栈的元素

在这里插入图片描述

从图中出栈的元素可以看出,对于后面遍历的元素,减少了这些出栈元素的匹配过程,降低了时间复杂度。

Java语言的代码

    public static int[] dailyTemperatures2(int[] T) {
        Stack<Integer> s = new Stack<>();//栈中存储索引值,对应的栈顶的元素最小
        int[] ans = new int[T.length];//结果输出的数组,初始化都是0
        for(int i = T.length - 1;i >= 0;--i){//从后向前遍历数组
            while (!s.empty() && T[i] >= T[s.peek()])s.pop();//栈不空,栈顶索引对应的元素小于等于当前元素,出栈,避免之后的元素重复计算
            ans[i] = s.empty()? 0 : s.peek() - i;//栈中元素是否为空,不空就是栈顶元素减去当前索引
            s.push(i);//当前元素入栈
        }
        return ans;
    }

Python3代码:

    def dailyTemperatures(self, T: List[int]) -> List[int]:
        ans = [0] * len(T)  # 初始化结果数组
        stack = []  # 存储索引的单调栈
        for i in range(len(T) - 1, -1, -1): # 从后向前遍历,步长-1,
            while stack and T[i] >= T[stack[-1]]:
                stack.pop()
            if stack:
                ans[i] = stack[-1] - i
            stack.append(i)
        return ans

c++代码:

    vector<int> dailyTemperatures(vector<int>& T) {
        vector<int> ans(T.size(),0);//初始化结果栈
        stack<int> s;
        for(int i = T.size() - 1;i >= 0;i--){
            while(!s.empty() && T[s.top()] <= T[i])s.pop();
            ans[i] = s.empty() ? 0 : s.top() - i;
            s.push(i);
        } 
        return ans;
    }

虽然方法是一样的,但是python3和c++的代码用时明显大于java的,不是很懂,欢迎大佬补充

归纳单调栈解决几种问题

通过该题的步骤我们大致可以归纳一下单调栈解决的几类问题

  • 如果是比该元素更大的元素,使用单调递增栈,否则,使用单调递减栈;
  • 对于严格单调还是非严格单调,具体看题目要求,对于相等的元素应该如何处理

其他应用单调栈的题目:

496.下一个更大的元素I
503.下一个更大的元素II
42.接雨水
901.股票价格跨度
239.滑动窗口最大值
84.柱状图中最大的矩形

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值