单调队列 Monotonic Queue应用:leetcode(未完待续)

239. Sliding Window Maximum

581: https://leetcode.com/problems/shortest-unsorted-continuous-subarray/

862: https://leetcode.com/problems/shortest-subarray-with-sum-at-least-k/
496: https://leetcode.com/problems/next-greater-element-i/
84: https://leetcode.com/problems/largest-rectangle-in-histogram/

 

239:最主要是push

class Solution {
public:
    //单调队列:单调递增或单调递减队列
    class MQ {
            deque<pair<int,int>> mq;
            //first为数组下标。second为push该数时前面remove掉的数的个数(保持MQ中第一个元素为最大值,可能会pop掉之前的好几个元素)
            //那么,每次在front_pop时,second为0,才会pop该元素,否则,second-1(其实可以理解second为队列中总的元素数)
        public:
            void push(int val){
                //如果为空队列,直接push
                if(mq.empty()){
                    mq.push_back(make_pair(val,0));
                    return ;
                } 
                //双端队列不为空,remove掉前面小于该val的数,保证最大的为队头.
                //而且保证队列中为递减,只保留最大、次大。。。
                //3 1 在队列,现在push 2: 如何做?
                int cnt = 0;
                while(!mq.empty() && mq.back().first < val){
                    cnt = cnt + mq.back().second+1;
                    mq.pop_back();
                }
                mq.push_back(make_pair(val,cnt));
            }
            //pop的时候,pop队头,但是要保证队头元素的second属性为0.
            void pop(){
                if(!mq.empty() && mq.front().second){
                    mq.front().second--;
                    return ;
                }
                if(!mq.empty())  mq.pop_front();
            }
            int getMax(){
                return mq.front().first;
            }
    };
    
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        //每一次push,getMax,pop
        int n = nums.size();
        vector<int> res;
        deque<pair<int,int>> dq;
        MQ mq;
        for(int i=0;i<n;i++){
            mq.push(nums[i]);
            if(i >= k-1){
                res.push_back(mq.getMax());
                mq.pop();
            } 
        }
        return res;
    }
};

 

So we maintain a monotonic array with index increasing and value decreasing, because smaller elements like A[l] on the left are useless.

For example, with sliding window of fixed length 3,

 A = [3, 1, 4, 3, 8] => monotonic queue is like [3], [3, 1], [4], [4, 3], [8]

when element 4 enters, we remove [3, 1] because they are on the left and smaller than 4, no chance being chosen as the max element.

The head of the increasing queue is the running max!

class Solution {
public:
    //mq存nums数组下标
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        deque<int> mq;
        vector<int> res;
        int n = nums.size();
        for(int i=0;i<n;i++){
            if(!mq.empty() && i-mq.front() >=k) mq.pop_front();
            //push进去
            while(!mq.empty() && nums[mq.back()] < nums[i]){
                mq.pop_back();
            }
            mq.push_back(i);
            if(i >= k-1) res.push_back(nums[mq.front()]);
        }
        return res;
    }
};

 

581: 最短无序连续子数组

class Solution {
public:
    //基本方法利用排序,前后进行数组对比
    int findUnsortedSubarray(vector<int>& nums) {
        int n = nums.size();
        int res = n;
        int s=n,e=-1;
        //1 3 5 6 7 2 8 9
        //超时,优化。s:位于数组中最左边的其后有元素小于nums[s]的位置,e:位于数组最右边其前有元素大于nums[e]的位置
        // for(int i=0;i<n;i++){
        //     for(int j=i+1;j<n;j++){
        //         if(nums[i] > nums[j]) {
        //             s = i;
        //             e = j;
        //         }
        //         if(s != e){
        //             start.push_back(s);
        //             end.push_back(e);
        //         }
        //     }
        // }
        //return start.size()==0 ? 0:*max_element(end.begin(),end.end())-start.front()+1;
        //利用栈: 找出最左和左右位置。
        //当nums[j] >= 栈顶 一直push(升序),当num[j] < 栈顶开始pop直到num[j]>栈顶,此时栈顶位置为s(所有都遍历完)
        //同理:反序一遍找e
        stack<int> sta;
        for(int i=0;i<n;i++){
            while(!sta.empty() && nums[i] < nums[sta.top()]) {
                s = min(s,sta.top());
                sta.pop();
            }
            sta.push(i);
        }
        while(!sta.empty()) sta.pop();
        for(int i=n-1;i>=0;i--){
            while(!sta.empty() && nums[i] > nums[sta.top()]) {
                e = max(e,sta.top());
                sta.pop();
            }
            sta.push(i);
        }
        return s-e >n ? 0 : e-s+1;
    }
};

上述算法O(N) spce,time

 

优化:找出无序数组部分的最小值和最大值,再找到小于最小值的位置和大于最大值的位置(直接即为无序部分)

我们再次遍历 numsnums 数组并通过与其他元素进行比较,来找到 minmin 和 maxmax 在原数组中的正确位置。我们只需要从头开始找到第一个大于 minmin 的元素,从尾开始找到第一个小于 maxmax 的元素,它们之间就是最短无序子数组。

 

 

class Solution {
public:
    //找数组中无序部分最小与最大
    //再找到小于min的位置,大于max的位置
    int findUnsortedSubarray(vector<int>& nums) {
        int beg = -1,end = -2;
        int n = nums.size();
        int mn = INT_MAX,mx = INT_MIN;
        int flag = false;
        for(int i=1;i<n;i++){
            if(nums[i] < nums[i-1]) flag = true;
            //后面都是无序部分
            if(flag){
                if(nums[i] < mn) mn = nums[i];
            }
            
        }
        flag = false;
        for(int i=n-2;i>=0;i--){
            if(nums[i] > nums[i+1]) flag = true;
            if(flag){
                if(nums[i] > mx) mx = nums[i];
            }
        }
        //找出小于min的位置和大于max的位置,这个位置直接都无序
        int l = n,r = -1;
        for(int i=0;i<n;i++){
            if(nums[i] > mn) l = min(l,i);
            if(nums[i] < mx) r = max(r,i);
        }
        return l-r > n ? 0:r-l+1;
    }
};

 

 

862: Shortest Subarray with Sum at Least K

class Solution {
public:
    //deque+前缀和.
    //前缀和   {pre[i] <= pre[j]-K}
    int shortestSubarray(vector<int>& A, int K) {
        int n = A.size();
        int res = n+1;
        //最主要的是首先得想到前缀和:这个问题典型的前缀和
        vector<int> pre;
        int sum = 0;
        for(int i=0;i<n;i++){
            sum += A[i];
            pre.push_back(sum);
        }
        //问题转化为:pre[j]-pre[i] >= K.即对于当前的pre[j]来说,找一个pre[i]最小,且距离j最近
        //左侧 最近最小的元素(单调递减序列)
        deque<int> dq;
        for(int i=0;i<n;i++){
            if(pre[i] >= K) res = min(res,i+1);
            //首先进行结果判断
            while(!dq.empty() && pre[i]-pre[dq.front()]>=K){
                res = min(res,i-dq.front());
                dq.pop_front();
            }
            //将当前pre[i] 入队尾,尾部比当前pre[i]大的元素无机会再留在队列内le
            while(!dq.empty() && pre[i] <= pre[dq.back()]){
                dq.pop_back();
            }
            dq.push_back(i);
        }
        return res == n+1 ? -1:res;
    }
};

 

 

 

496: 

posted on 2020-11-22 15:32  wsw_seu  阅读(149)  评论(0编辑  收藏  举报

导航