C++ 单调栈

单调栈简介

什么时候用单调栈

当题目给出一维数组,且要求我们求出数组元素的左边或者右边第一个比自己大或者比自己小的数组元素的下标或者数组元素本身的时候。注意,这里就有四种情况:左边比自己大,左边比自己小,右边比自己大,右边比自己小。

单调栈的原理

  • 单调栈顾名思义是单调递增的栈,递增方向是从栈顶到栈底,也就是说栈顶元素小,栈底元素大。 这种情况就是求一个元素右边第一个更大元素。
  • 反之,要求一个元素右边第一个更小元素,单调栈是单调递减的栈,递增方向是从栈顶到栈底,也就是说栈顶元素大,栈底元素小。

另外,我们在单调栈里存储元素下标即可。那么元素是怎么进出单调栈的?总的原则是保持单调栈从栈顶至栈底单调递增。这里我们以求一个元素右边第一个更大元素为例,首先将第一个元素的下标入栈,接下来遍历数组,分为三种情况

  • 当前遍历的元素V[i]小于栈顶元素V[st.top()],i加入单调栈;
  • 当前遍历的元素V[i]等于栈顶元素V[st.top()],i加入单调栈;
  • 当前遍历的元素V[i]大于栈顶元素V[st.top()],单调栈的元素出栈,直至碰到比当前元素小于等于的元素,每次st.pop()之前都要记录结果res[st.top()]=i-st.top(),res[st.top()]=i-st.top(),res[st.top()]=i-st.top(),res[st.top()]=i-st.top(),res[st.top()]=i-st.top();最后i加入单调栈。

例题解析

每日温度

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

解析:题目要求比当天气温更高的一天--右边比自己大,则使用单调栈!

cpp 复制代码
class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        auto sz=temperatures.size();
        vector<int> res(sz,0);
        stack<int> st;
        st.push(0);
        for(int i=1;i<sz;i++)
        {
            if(temperatures[i]<temperatures[st.top()])
            {
                st.push(i);
            }
            else if(temperatures[i]==temperatures[st.top()])
            {
                st.push(i);
            }
            else
            {
                while(!st.empty()&&temperatures[st.top()]<temperatures[i])
                {
                    res[st.top()]=i-st.top();
                    st.pop();               
                }
                st.push(i);
            }
        }
        return res;
    }
};

时间复杂度和空间复杂度都是O(N)。

下一个更大元素I

nums1 中数字 x下一个更大元素 是指 xnums2 中对应位置 右侧第一个x大的元素。

给你两个没有重复元素 的数组 nums1nums2 ,下标从 0 开始计数,其中nums1nums2 的子集。

对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j]下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1

返回一个长度为 nums1.length 的数组ans作为答案,满足ans[i]是如上所述的 下一个更大元素

解析:nums1是nums2的子集,且顺序是打乱的。我们要先知道nums1元素在nums2元素的位置,因此用unordered_map进行映射即可。接下来遍历nums2,如果栈中的元素存在于nums1,则记录结果。

cpp 复制代码
                    if(hash.count(nums2[st.top()])>0)// 栈中的元素在nums1中
                    {
                        int index=hash[nums2[st.top()]];
                        res[index]=nums2[i];// 当前遍历的元素
                    }
cpp 复制代码
class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        int sz1=nums1.size(),sz2=nums2.size();
        unordered_map<int,int> hash;
        stack<int> st;
        st.push(0);
        vector<int> res(sz1,-1);
        for(int i=0;i<sz1;i++)
        {
            hash[nums1[i]]=i;
        }
        for(int i=1;i<sz2;i++)
        {
            if(nums2[i]<nums2[st.top()])
            {
                st.push(i);
            }
            else if(nums2[i]==nums2[st.top()])
            {
                st.push(i);
            }
            else
            {
                while(!st.empty()&&nums2[i]>nums2[st.top()])
                {
                    if(hash.count(nums2[st.top()])>0)// 栈中的元素在nums1中
                    {
                        int index=hash[nums2[st.top()]];
                        res[index]=nums2[i];// 当前遍历的元素
                    }
                    st.pop();
                }
                st.push(i);
            }
        }
        return res;
    }
};

下一个更大的元素II

给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。

解析:本题较上一题,思路一样,处理比较简单。碰到循环,基本思路就是%size。本题还需注意的是要遍历到size*2,才能达到循环的效果。

cpp 复制代码
class Solution {
public:
    vector<int> nextGreaterElements(vector<int>& nums) {
        int sz=nums.size();
        vector<int> res(sz,-1);
        stack<int> st;
        st.push(0);
        for(int i=1;i<sz*2;i++)
        {
            if(nums[i%sz]<nums[st.top()])
            {
                st.push(i%sz);
            }
            else if(nums[i%sz]==nums[st.top()])
            {
                st.push(i%sz);
            }
            else
            {
                while(!st.empty()&&nums[i%sz]>nums[st.top()])
                {
                    res[st.top()]=nums[i%sz];
                    st.pop();
                }
                st.push(i%sz);
            }
        }
        return res;
    }
};

接雨水

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

解析:本题个人觉得双指针思路较简单。只需找到当前位置左边和右边的最大高度,取二者的最小值-当前位置高度即可。注意0和size-1的位置无需处理。

cpp 复制代码
class Solution {
public:
    int trap(vector<int>& height) {
        int res=0;
        int sz=height.size();
        for(int i=1;i<sz-1;i++)
        {
            int h=height[i];
            int l=h,r=h;
            for(int j=i-1;j>=0;j--)
            {
                if(height[j]>l)
                    l=height[j];
            }
            for(int j=i+1;j<sz;j++)
            {
                if(height[j]>r)
                    r=height[j];
            }
            int ans=min(l,r)-h;
            if(ans>0)   res+=ans;
        }
        return res;
    }
};

但时间复杂度为O(n^2),测试用例的最后一个超时了。

或者我们运用动态规划的思想提前把当前高度左边和右边最高求一下,用空间换时间。

cpp 复制代码
class Solution {
public:
    int trap(vector<int>& height) {
        int sz=height.size();
        vector<int> maxL(sz);// 每个柱子左边最大的
        vector<int> maxR(sz);// 每个柱子右边最大的
        int res=0;
        // 初始化
        maxL[0]=height[0];
        for(int i=1;i<sz;i++)
        {
            maxL[i]=max(maxL[i-1],height[i]);
        }
        // 初始化
        maxR[sz-1]=height[sz-1];
        for(int i=sz-2;i>=0;i--)
        {
            maxR[i]=max(height[i],maxR[i+1]);
        }
        for(int i=1;i<sz-1;i++)
        {
            int tmp=min(maxL[i],maxR[i])-height[i];
            if(tmp>0)
                res+=tmp;
        }
        return res;
    }
};

单调栈

相关推荐
微澜-23 分钟前
编译以前项目更改在x64下面时报错:函数“PVOID GetCurrentFiber(void)”已有主体
c++
YuanLiu_22729 分钟前
代码随想录算法训练营第十三天(递归遍历;迭代遍历;统一迭代;层序遍历)
java·数据结构·笔记·算法·leetcode
闻缺陷则喜何志丹31 分钟前
【C++动态规划】1411. 给 N x 3 网格图涂色的方案数|1844
c++·算法·动态规划·力扣·网格·数目·涂色
仙俊红1 小时前
快速运行openMMOCR
深度学习·算法
-Max-静-1 小时前
Paddle Inference部署推理(十八)
人工智能·windows·深度学习·算法·paddle·推理 部署
熬夜学编程的小王1 小时前
【C++篇】解锁C++模板的魔法:从万能钥匙到精准雕刻
c++·进阶模版·c++模版·类模版实例化·函数模版实例化
Octopus20771 小时前
【C++】读取数量不定的输入数据
开发语言·c++·笔记·学习
忘梓.1 小时前
C嘎嘎探索篇:栈与队列的交响:C++中的结构艺术
c语言·开发语言·c++·
f狐0狸x1 小时前
【数据结构实战篇】用C语言实现你的私有队列
c语言·数据结构·链表··队列
十五年专注C++开发2 小时前
C++中的链式操作原理与应用(一)
开发语言·c++·设计模式