带你手撕单调栈(一)

单调栈

是么时候用单调栈

一定要记住的是数据结构一定是辅助我们解题的,一定是在暴力解法的方式下进行优化(其实也就是以空间换时间)

单调栈一定要符合我们在处理数据的时候先进后出的这样一个特性,就像是我们用优先队列的时候一定要满足处理顺序的先进先出的特性

怎么用好单调栈

用好单调栈要明白几个问题

  • 入栈的具体含义是什么
  • 出栈的具体含义是什么
  • 什么时候入栈
  • 什么时候出栈
  • 入栈的顺序:记住栈是正着入栈倒着出栈,倒着入栈正着出栈
  • 怎么设置哨兵点

模板

vector<int> answer(vector<int>& nums) {
    vector<int> ans(nums.size());
    stack<int> st;
    for(int i=nums.size()-1;i>=0;i--) {
        while(!st.empty() && st.top()<=nums[i]) { // 这里的第二个条件要根据实际情况改变
            st.pop();
        }
        ans[i]=st.empty()? 0 : st.top();
        st.push(nums[i])
    }
    return ans;
}

其实也可以正着遍历,也可以加入哨兵点来更好的解决问题

也就是说,for()从后往前遍历。因为我们借助了栈的结构,倒着入栈其实就是正着出栈

例题

739. 每日温度

难度中等817

请根据每日 气温 列表 temperatures ,请计算在每一天需要等几天才会有更高的温度。如果气温在这之后都不会升高,请在该位置用 0 来代替。

示例 1:

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

示例 2:

输入: temperatures = [30,40,50,60]
输出: [1,1,1,0]

示例 3:

输入: temperatures = [30,60,90]
输出: [1,1,0]

提示:

  • 1 <= temperatures.length <= 105
  • 30 <= temperatures[i] <= 100

拿到题目不慌,先进行一波分析,任何问题都有一定的特性,拿到问题首先想的一定是暴力怎么取解决再找出问题的特点进行优化

问题的暴力解法:
	就是每一个数字往后遍历去找时间复杂度为o(n*n)
	这个时候为什么每一次都往后找,因为没有记住后面比他大的有哪些值
	这个时候分析一下,只有73能不能知道问题的解-----不能
	73,74呢-------可以73位置处的答案就是1,但是74位置的答案你就不知道了
	这时候就体现出了一个特点
	先访问的点反而后得出结果。
	这个就很符合栈的处理特点,所以我们用单调栈来进行解决

正序

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        // 正序遍历解题

        vector<int> ans(temperatures.size(),0);
        stack<int> st;
        for(int i=0;i<temperatures.size();i++) {
            while(!st.empty() && temperatures[i]>temperatures[st.top()]) {
                ans[st.top()]=i-st.top();
                st.pop();
            }

            st.push(i);
        }
        return ans;
        

    }
};

倒序+哨兵

class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        // 思路
        /* 
            这个应该从后往前取遍历,因为前面的结果依赖后面,后面的结果更容易得到
            之后可以看出,这个符合栈的处理方式,先遍历的数据后得出结果
        
         */

        vector<int> res(temperatures.size()+1);
        stack<int> st;
        temperatures.push_back(0);
        for(int i=temperatures.size()-1;i>=0;i--) {
            while(!st.empty() && temperatures[i]>=temperatures[st.top()]) {
                st.pop();
            }

            res[i]=st.empty()? 0 : st.top()-i;
            st.push(i);

        }
        res.pop_back();
        return res;


    }
};

496. 下一个更大元素 I

难度简单452

给你两个 没有重复元素 的数组 nums1nums2 ,其中nums1nums2 的子集。

请你找出 nums1 中每个元素在 nums2 中的下一个比其大的值。

nums1 中数字 x 的下一个更大元素是指 xnums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1

示例 1:

输入: nums1 = [4,1,2], nums2 = [1,3,4,2].
输出: [-1,3,-1]
解释:
    对于 num1 中的数字 4 ,你无法在第二个数组中找到下一个更大的数字,因此输出 -1 。
    对于 num1 中的数字 1 ,第二个数组中数字1右边的下一个较大数字是 3 。
    对于 num1 中的数字 2 ,第二个数组中没有下一个更大的数字,因此输出 -1 。

示例 2:

输入: nums1 = [2,4], nums2 = [1,2,3,4].
输出: [3,-1]
解释:
    对于 num1 中的数字 2 ,第二个数组中的下一个较大数字是 3 。
    对于 num1 中的数字 4 ,第二个数组中没有下一个更大的数字,因此输出 -1 。
前序遍历
class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        // 很明显符合先进后出这么个习惯
        // 理解题意,nums1是nums2的子集也就是说nums1的长度<=nums2的长度
        // 可以先用单调栈去计算好nums2的每一个数的最大值,而且还没有重复元素
        map<int,int> mp; // [数字,下标]

        for(int i=0;i<nums2.size();i++) {
            mp[nums2[i]]=i;
        }

        vector<int> ans(nums2.size(),-1);
        stack<int> st;
        for(int i=0;i<nums2.size();i++) {

            while(!st.empty() && nums2[st.top()]<nums2[i]) {
                ans[st.top()]=nums2[i];
                st.pop();

            }
            st.push(i);

        }


        vector<int> res(nums1.size());
        for(int i=0;i<nums1.size();i++) {
            res[i]=ans[mp[nums1[i]]];
        }
        return res;

    }
};
后序遍历+哨兵
class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        // 很明显符合先进后出这么个习惯
        // 理解题意,nums1是nums2的子集也就是说nums1的长度<=nums2的长度
        // 可以先用单调栈去计算好nums2的每一个数的最大值,而且还没有重复元素
        map<int,int> mp; // [数字,下标]

        for(int i=0;i<nums2.size();i++) {
            mp[nums2[i]]=i;
        }

        nums2.push_back(-1);
        vector<int> ans(nums2.size(),-1);
        stack<int> st;

        for(int i=nums2.size()-1;i>=0;i--) {
            while(!st.empty() && nums2[st.top()]<=nums2[i] ) {
                st.pop();
            }
            // 这个时候栈顶比数字小的都被移除,这是一个非严格的单调栈
            ans[i]=st.empty() ? -1: nums2[st.top()];
            st.push(i);

        }
        ans.pop_back();
        vector<int> res(nums1.size());
        for(int i=0;i<nums1.size();i++) {
            res[i]=ans[mp[nums1[i]]];
        }
        return res;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值