单调栈学习

单调栈

单调递增栈:
①在一个队列中针对每一个元素从它右边寻找第一个比它小的元素
②在一个队列中针对每一个元素从它左边寻找第一个比它小的元素
单调递减栈:
①在一个队列中针对每一个元素从它右边寻找第一个比它大的元素
②在一个队列中针对每一个元素从它左边寻找第一个比它大的元素
单调栈何时用:为任意一个元素找左边和右边第一个比自己大/小的位置用单调栈。
子数组的最小值之和
求连续子数组的最小值之和。
首先从左边出发寻找以arr[i]为右端点而且arr[i]是最小元素的序列个数,从右边出现寻找以arr[i]为左端点而且arr[i]是最小元素的序列个数,两者相乘就得到以arr[i]为最小元素的序列个数。
这样我们要找到当前元素前一个比它小的元素,以及后一个比它小的元素,所以使用单调递增栈。
如果栈内元素大于当前元素,把它pop掉。把i-s.top()记录在left数组中。
0 1 2 3 4
11 81 94 43 3
-1 0 1 0 -1
以当前index减去前一个最小元素下标就能得到左边以该元素为最小值的序列数。如0-(-1)=1就是11的序列。
从后遍历同理。
这里要注意的是,判断栈内元素和当前元素时,等于需不需要加上。思路为最小元素,理应也要去掉等于当前元素的前一个元素,但是存在元素重复的情况。
如71 55 82 55
按照最小元素的方法,也就是前后遍历时都加上相等判断
left为-1 -1 1 -1
right为1 4 3 4
在第一个55时,left为-1,序列为71 55和55共1-(-1)=2个,right如果加上等于就是4(自己为最小),序列为4-1共3个,分别为55 82 55、55 82、55。两者相乘为6,分别为71 55 82 55、71 55 82、71 55、55 82 55、55 82、55.

然后再看看和它相等的第一个55.按照最小元素的方法,left为-1,序列共4个,分别为71 55 82 55、55 82 55、82 55、55.right为4,序列共1个55.两者相乘为71 55 82 5555 82 55、82 55、55.可以看到有序列被重复统计了。比如在向前遍历第一个55的时候,把相等的元素也考虑在内,剔除序列相等的元素,这样right为后一个55的下标3,这样序列为2,不计算重复的序列。假设当前元素为最右边且最小的元素,这样才可以构造互不相交的子序列。
对于上面的情况,向后遍历和向前遍历是一样的,因为相等元素是对称的,所以在其中一次遍历剔除相等元素都行。

int sumSubarrayMins(vector<int>& arr) {
        int n=arr.size();
        if(n==1){
            return arr[0];
        }
        long long res=0;
        long long mod = 1e9 + 7;
        vector<int> st;
        vector<int> left(n),right(n);
        for(int i=0;i<n;i++){
            while(!st.empty()&&arr[st.back()]>=arr[i]){
                st.pop_back();
            }
            left[i]=i-(st.empty()? -1 :st.back());
            st.push_back(i);
        }
        st.clear();
        for(int i=n-1;i>=0;i--){
            while(!st.empty()&&arr[st.back()]>arr[i]){
                st.pop_back();
            }
            right[i]=(st.empty()? n :st.back())-i;
            st.push_back(i);
        }
        for(int i=0;i<n;i++){
            res=(res+(long long)arr[i]*left[i]*right[i])%mod;
        }
        return res;
    }

下一个更大元素 I
这道题就是简单的单调栈加上哈希表的应用。

vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        vector<int> st;
        int n=nums2.size();
        vector<int> right(n);
        map<int,int> mp;
        for(int i=n-1;i>=0;i--){
            mp.insert(make_pair(nums2[i],i));
            while(!st.empty()&&nums2[st.back()]<nums2[i]){
                st.pop_back();
            }
            right[i]=(st.empty()?-1:st.back());
            st.push_back(i);
        }
        vector<int> res;
        for(int i=0;i<nums1.size();i++){
            int index=mp[nums1[i]];
            if(right[index]!=-1){
                res.push_back(nums2[right[index]]);
            }else{
                res.push_back(right[index]);
            }
            
        }
        return res;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值