zoukankan      html  css  js  c++  java
  • 单调栈与单调队列

    单调栈

    特点

    栈内的元素单调递增或者单调递减,可以求出数列中所有数的左边或右边第一个比其大或小的元素,总时间复杂度为(O(n))

    例子

    单调栈中一般存索引
    如何实现呢?
    以单调递增栈求需入栈元素X的两边第一个比X小的值为例,每次有元素a[i] = X进栈时,

        // a[i] = X
        while (栈不为空 且 栈顶元素s[top] >= a[i])
            栈顶元素出栈
        // 直到栈顶元素s[top] < X或者栈为空
        
        // X入栈
        s.push(a[i]) 
    

    当栈顶元素比a[i]大时,对于未来的a[i+1], a[i+2],...来说,比a[i]更大的栈顶元素与a[i]相比,选择a[i]始终更优
    以4,2,3为例,最开始4入栈, 当需要2入栈时,由于2比4小,对于2之后的元素3来说,其左边第一个数选择了2一定不会选择4,2始终比4更优

    回到正题,根据单调递增栈的特性可以得出
    当X入栈时(对应s.push(a[i]) ),此时栈顶索引元素所指的值就是X左边第一个比X小的值, 栈为空则没有
    当栈顶出栈时(对应while),此时的X为右边第一个比栈顶元素小的值
    例题84. Largest Rectangle in Histogram

    class Solution {
    public:
        int largestRectangleArea(vector<int>& heights) {
            stack<int> s;
            int area = 0;
            heights.push_back(0);
            for(int i = 0; i < heights.size(); i++){
                while(!s.empty() && heights[s.top()] >= heights[i]) {
                    int j = s.top();
                    s.pop();
                    area = max(area, heights[j] * (s.size() == 0 ? i: (i - (s.top() + 1)) ));
                }
                
                s.push(i);
            }
            
            return area;
        }
    };
    

    85. Maximal Rectangle
    与上题类似,需要处理一下

    单调队列

    特点

    队列中元素单调,队首,队尾可以出队,队尾可以入队
    区间最值问题可以使用ST表和线段树来解决,但经常用于多次查询,时间复杂度为(O(nlg(n)))
    如果是类似于滑动窗口的区间最值,则可以用单调队列来解决,时间复杂度为(O(n))
    例题239. Sliding Window Maximum
    这是个单调递减队列,队列维护滑动窗口,队列中位于前面且比后面小的一定不会是最大值,所以入队时可以踢出
    理解了单调栈,单调队列也比较好理解
    单调队列其他题目

    class Solution {
    public:
        vector<int> maxSlidingWindow(vector<int>& nums, int k) {
            deque<int> q;
            vector<int> v;
            for(int i = 0; i < k - 1; i++){
                while( !q.empty() && nums[q.back()] <= nums[i]) q.pop_back();
                
                q.push_back(i);
            }
            
            for(int i = k-1; i < nums.size(); i++){ 
                while( !q.empty() && nums[q.back()] <= nums[i]) q.pop_back();
                q.push_back(i);
                while( !q.empty() &&  i - q.front() >= k) q.pop_front();
                v.push_back(nums[q.front()]);
            }
            
            return v;
        }
    };
    

    862. Shortest Subarray with Sum at Least K

    class Solution {
    public:
        int shortestSubarray(vector<int>& A, int K) {
            int sum = 0;
            deque<int> q;
            int l = A.size() + 1;
    
            vector<int> B(l, 0);
            B[0] = 0;
            for(int i = 0; i < A.size(); i++) B[i+1] = B[i] + A[i];
            for(int i = 0; i < B.size(); i++){
              
               
                
                
                while(!q.empty() && B[i] <= B[q.back()]) q.pop_back();
                q.push_back(i);
                while(!q.empty() && B[i] - B[q.front()] >= K) {
                    l = min(l, i - q.front());
                    q.pop_front();
                }
                
            }
            
            return l == A.size() + 1 ? -1 : l;
        }
    };  
    

    使用单调队列的基本步骤大致为

    1.满足单调性
    
    当遍历到a[i]时,需要将a[i]入栈,首先从队列弹出在未来的遍历中一定比a[i]劣的值
    在滑动窗口中的
    while( !q.empty() && nums[q.back()] <= nums[i]) q.pop_back();
    表示在未来的滑动窗口中一定不会使用比之前小于等于nums[i]的值
    
    与862题中的
    while(!q.empty() && B[i] <= B[q.back()]) q.pop_back();
    在未来的前缀和中如果需要B[i]为首,那么B[i]越小越好,在B[i]之前且小于等于B[i]的前缀和比B[i]劣
    
    2.i入队列
    
    3.满足区间具体约束
    
    在滑动窗口中
    具体的约束为区间长度
    while( !q.empty() &&  i - q.front() >= k) q.pop_front();
    
    在862题中表示为
    while(!q.empty() && B[i] - B[q.front()] >= K) {
                l = min(l, i - q.front());
                q.pop_front();
            }
    
  • 相关阅读:
    calendar.getTimeInMillis() 和 System.currentTimeMillis() 的区别
    微信小程序中使用 <web-view> 内嵌 H5 时,登录问题的处理方法
    小程序 TabBar 定制
    webpack 代码优化压缩方法
    react-router v4 按需加载的配置方法
    axios发送post请求,如何提交表单数据?
    react中键盘enter事件处理
    常用证件正则表达式
    react中input自动聚焦问题
    React Router v4 页面传值的三种方法
  • 原文地址:https://www.cnblogs.com/qbits/p/10934055.html
Copyright © 2011-2022 走看看