LeetCode刷题笔记 标准模板库巧解算法题 单调栈

单调栈通过维持栈内值的单调递增(递减)性,在整体 O(n) 的时间内处理需要大小比较的问题。

739 每日温度

给定每天的温度,求对于每一天需要等几天才可以等到更暖和的一天。如果该天之后不存在更暖和的天气,则记为 0。

输入是一个一维整数数组,输出是同样长度的整数数组,表示对于每天需要等待多少天。

输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]

解析:

​ 本题可以通过维持一个单调递减的栈,表示每天的温度;而本题要求的是当前温度到更高温度的天数差,所以栈中的元素表示的是温度对应的日期,即温度在数组中的位置。单调递减的栈,所以栈顶表示的是栈内最低温度的日期。

​ 从左向右遍历数组,对于日期 p ,如果 p 对应的温度高于栈顶的日期 q 对应的温度,则遇到了更暖和的一天,将 q 出栈并记录天数差为 p - q;如果 p 对应的温度低于栈顶的日期 q 对应的温度,或者栈为空,则将 p 压入栈中,然后继续考虑下一天的情况。

​ 遍历完数组之后,栈内剩余的日期对应的温度都是递减的,说明他们之后不存在更暖和的天气。

class Solution {
    
    
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
    
    
        stack<int> s;
        int len = temperatures.size();
        vector<int> ans(len);
        for(int i=0;i<len;++i){
    
    
            // 出栈栈内低于当前日期温度的日期,并计算天数差
            while(!s.empty()){
    
    
                int preTemp = temperatures[s.top()];
                // 如果栈顶日期温度大于当前日期温度,停止出栈
                if(preTemp >= temperatures[i]){
    
    
                    break;
                }
                ans[s.top()] = i - s.top();
                s.pop();
            }
            s.push(i);
        }
        // 遍历完成之后,还未出栈的日期之后没有更高的温度
        while(!s.empty()){
    
    
            ans[s.top()] = 0;
            s.pop();
        }
        return ans;
    }
};

503 下一个更大元素 II

给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。如果不存在,则输出 -1。

输入是一个一维整数数组,输出是同样长度的整数数组,表示对于原数组每位置的数下一个更大元素。

输入: [1,2,1]
输出: [2,-1,2]
解释: 第一个 1 的下一个更大的数是 2;数字 2 找不到下一个更大的数; 第二个 1 的下一个最大的数需要循环搜索,结果也是 2。

解析:

​ 和每日温度相似,本题也可以通过维持一个单调递减的栈,表示原数组元素位置。

​ 本题和739 每日温度的区别在于本题待遍历的数组是一个循环数组,遍历循环数组的常用方式是将这个循环的圈拉直,即复制该序列的前 n-1 个元素拼接在原序列的后面。即用一个原数组的拷贝与其自身拼接成一个数组,例如将ABCDEF循环数组拉直有ABCDEFABCDEF,就有了A到A、B倒B……F到F的循环。

​ 更简单可以避免增加空间开销,直接可以用模运算遍历数组,例如长度为 n 的循环数组元素表示为array[i%n]

​ 按照上述循环数组遍历方法,从左向右遍历,对于当前位置 p,如果 p 对应的元素值大于单调栈栈顶 q 对应的元素值,则 q 遇到的更大值,在 q 位置保留 p 对应的值。如果栈为空,或者p 对应的元素值小于单调栈栈顶 q 对应的元素值,则将 p 压入栈,然后考虑下一个元素。

​ 遍历完数组之后,栈内剩余的位置对应的元素值都是递减的,说明他们之后不存在更大的元素。

class Solution {
    
    
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
    
    
        stack<int> s;
        int len = nums.size();
        // ans都初始化成 -1 就不用再去考虑没有出栈的元素了
        vector<int> ans(len,-1);
        for(int i=0;i<2*len-1;++i){
    
    
            while(!s.empty()){
    
    
                int preVal = nums[s.top()%len];
                if(preVal >= nums[i%len]){
    
    
                    break;
                }
                ans[s.top()%len] = nums[i%len];
                s.pop();
            }
            s.push(i);
        }
        return ans;
    }
};

参考资料

LeetCode 101:和你一起轻松刷题(C++) 第 11 章 妙用数据结构

Guess you like

Origin blog.csdn.net/qq_41773806/article/details/120862183