Skip to content

单调队列 Monotone Queue

即保证队列内元素单调性的队列。

通常用于滑动窗口的极值问题。

//// c version (min)
int q[maxn]; // the monotone values inside the window 
int p[maxn]; // the indices
int h = 0, t = 0;
for (int i = 0; i < n; i++) {
    // remove time-out 
    while (h < t && p[h] <= i - k) h++;
    // maintain queue (cs[i] is the current val, remove vals larger than it.)
    while (t >= 1 && xs[i] < q[t-1]) t--;
    p[t] = i;
    q[t++] = xs[i];
    // now q[h] is the min value in window (i-k, i]
    ...;
}

//// stl version (min)
vector<int> q; // store values
deque<int> p; // store enqueue time
for (int i = 0; i < n; i++) {
    while (!p.empty() && p.front() <= i - k) p.pop_front();
    while (!p.empty() && xs[i] < q[p.back()]) p.pop_back(); // < or <= is both OK.
    p.push_back(i);
    q.push_back(xs[i]);
    // q[p.front()] is the min value in window (i-k, i]
    ...;
}

//// stl version (max)
vector<int> q; // store values
deque<int> p; // store enqueue time
for (int i = 0; i < n; i++) {
    while (!p.empty() && p.front() <= i - k) p.pop_front();
    while (!p.empty() && xs[i] > q[p.back()]) p.pop_back(); // > or >= is both OK.
    p.push_back(i);
    q.push_back(xs[i]);
    // q[p.front()] is the min value in window (i-k, i]
    ...;
}

滑动窗口极值

裸模板\(O(N)\)

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

另一种形式(deque不维护时间,直接维护最值)

class Solution {
public:
    struct monoque{
        deque<int> que;
        void push(int n){
            while(!que.empty() && que.back()<n) que.pop_back();   
            que.push_back(n);
        }
        int getmax(){ return que.front(); }
        void pop(int n){ 
            if(que.front() == n) que.pop_front(); 
        }
    };
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        int N = nums.size();
        vector<int> ans;
        monoque que;
        for(int i=0; i<N; i++){
            que.push(nums[i]);
            if(i>=k-1){
                ans.push_back(que.getmax());
                que.pop(nums[i-k+1]);
            }
        }
        return ans;
    }
};

使用Sparse Table的RMQ算法也可以\(O(N)\)

Maximum value in queue

deque直接记录当前窗口的最值。

class MaxQueue {
public:

    queue<int> q;
    deque<int> p;

    MaxQueue() {}

    int max_value() {
        if (q.empty()) return -1;
        return p.front();
    }

    void push_back(int value) {
        q.push(value);
        while (!p.empty() && value > p.back()) p.pop_back(); // must be >, cannot use >=
        p.push_back(value);
    }

    int pop_front() {
        if (q.empty()) return -1;
        int x = q.front(); q.pop();
        if (x == p.front()) p.pop_front();
        return x;
    }
};

Maximum Sum Circular Subarray

前缀和&滑动窗口:

class Solution {
public:
    int maxSubarraySumCircular(vector<int>& A) {
        int n = A.size();

        // copy
        vector<int> AA;
        for (int i = 0; i < n; i++) AA.push_back(A[i]);
        for (int i = 0; i < n; i++) AA.push_back(A[i]);

        // cumsum
        vector<int> cs(2*n+1, 0);
        for (int i = 1; i <= 2 * n; i++) {
            cs[i] = cs[i - 1] + AA[i - 1];
        }

        vector<int> q; // store values
        deque<int> p; // store enqueue time
        q.push_back(0);
        p.push_back(0);

        int ans = -0x3f3f3f3f;
        for (int i = 1; i <= 2 * n; i++) {
            // maintain queue
            while (!p.empty() && p.front() < i - n) p.pop_front();
            // maintain ans
            ans = max(ans, cs[i] - q[p.front()]);
            // maintain queue
            while (!p.empty() && cs[i] < q[p.back()]) p.pop_back();
            p.push_back(i);
            q.push_back(cs[i]);
        }
        return ans;

    }
};

Kadane Twice:

class Solution {
public:
    int maxSubarraySumCircular(vector<int>& A) {
        int n = A.size();
        // kadane 1
        int ans = A[0];
        int dp = A[0];
        for (int i = 1; i < n; i++) {
            dp = max(dp + A[i], A[i]);
            ans = max(ans, dp);
        }
        // kadane 2 (if at least 3 elements)
        int ans2 = ans;
        if (n >= 3) {
            int s = 0;
            for (int i = 0; i < n; i++) s += A[i];
            ans2 = A[1];
            dp = A[1];
            for (int i = 2; i < n - 1; i++) {
                dp = min(dp + A[i], A[i]);
                ans2 = min(ans2, dp);
            }
            ans2 = s - ans2;
        }
        // final
        return max(ans, ans2);
    }
};

The next larger element (monotone stack)

给你两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。请你找出 nums1 中每个元素在 nums2 中的下一个比其大的值。

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

倒序遍历+单调栈。\(O(n+m)\)时间复杂度。

class Solution {
public:
    vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
        // track next larger element for all pos in nums2
        unordered_map<int,int> hashmap;
        stack<int> st;
        for (int i = nums2.size() - 1; i >= 0; --i) {
            int num = nums2[i];
            while (!st.empty() && num >= st.top()) st.pop();
            hashmap[num] = st.empty() ? -1 : st.top();
            st.push(num);
        }
        // retrieve answer
        vector<int> res(nums1.size());
        for (int i = 0; i < nums1.size(); ++i) {
            res[i] = hashmap[nums1[i]];
        }
        return res;
    }
};