单调队列与单调栈

RMQ问题

RMQ(x, y) 就是询问数组 [x, y] 区间内部的最小值
例如:RMQ(0, 3) = 1, RMQ(3, 7) = 2
现在,固定询问区间的尾部,例如:RMQ(x, 7)
请思考,如下序列中最少记录几个元素,就可以满足RMQ(x, 7) 的任何需求

入队操作: 队尾入队,会把之前破坏单调性的元素都从队尾移出(维护单调性)
出队操作: 如果队首元素超出区间范围,就将元素从队首出队
元素性质: 队首元素,永远是当前维护区间的(最大/最小)值
序列中的每⼀个元素,在依次入队的过程中,每个元素都『黄』过

单调队列代码演示

void output(vector<int> &arr) {
    int n = arr.size(), len = 0
    for (int i = 0; i < n; i++) {
        len += cout.width(3), cout << i;
    }
    cout << endl;
    for (int i = 0; i < len; i++) cout << "-";
    cout << endl;
    for (int i = 0; i < n; i++) {
        len += cout.width(3), cout << arr[i];
    }
    cout << endl;
    return;
}

int main() {
    int n, k;
    cin >> n >> k;
    vector<int> arr;
    deque<int> q;
    for (int i = 0, a; i < n; i++) {
        cin >> a;
        arr.push_back(a);
    }
    output(arr);
    for (int i = 0; i < n; i++) {
        while (!q.empty() && arr[q.back()] > arr[i]) q.pop_back();
        q.push_back(i); // index
        if (i - q.front() == k) q.pop_front();
        cout << "[" << max(i - k + 1, 0) << ", " << i << "] = arr[" 
             << q.front() << "] = " << arr[q.front()] << endl;
    }
    return 0;
}

单调栈

问题引入: 给一个序列,求序列中,每个元素左侧,第一个小于它的元素
观察单调队列的逻辑模型,每个黄色元素左侧第一个小于它的元素, 是前⼀个黄色元素,根据入队列过程中,每⼀个元素都『黄』过, 那么将所有元素依次入队,当前元素在队列中的前⼀个元素,即是问题所求。
这种不从头部出的结构,我们叫他【单调栈】

单调递增:最近小于关系
单调递减:最近大于关系

总结:
单调队列: 擅长维护区间【最大/最小】值,最小值对应单调递增队列
单调栈: 擅长维护最近【大于/小于】关系 从左侧先⼊栈,就是维护左侧最近关系 从右侧先⼊栈,就是维护右侧最近关系

代码演示

void output(const vector<int> &arr) {
    int n = arr.size(), len = 0;
    for (int i = 0; i < n; i++) {
        len += cout.width(3), cout << i;
    }
    cout << endl;
    for (int i = 0; i < len; i++) cout << "-";
    cout << endl;
    for (int i = 0; i < n; i++) {
        len += cout.width(3), cout << arr[i];
    }
    cout << endl;
}

int main() {
    int n;
    cin >> n;
    vector<int> arr;
    arr.push_back(-1); // 添加哨兵到数组开头
    stack<int> s;
    for (int i = 0, a; i < n; i++) {
        cin >> a;
        arr.push_back(a);
    }
    arr.push_back(-1); // 添加哨兵到数组末尾
    vector<int> l(arr.size()), r(arr.size()); // 左右边界数组
    output(arr);

    // 计算右边界
    for (int i = 0, I = arr.size(); i < I; i++) {
        while (!s.empty() && arr[s.top()] > arr[i]) {
            r[s.top()] = i;
            s.pop();
        }
        s.push(i);
    }

    // 计算左边界
    while (!s.empty()) s.pop();
    for (int i = arr.size() - 1; i >= 0; i--) {
        while (!s.empty() && arr[s.top()] > arr[i]) {
            l[s.top()] = i;
            s.pop();
        }
        s.push(i);
    }

    // 输出结果
    for (int i = 1; i <= n; i++) {
        cout << "arr[" << i << "] = " << arr[i] 
             << ", right : arr[" << r[i] << "] = " << arr[r[i]] 
             << ", left : arr[" << l[i] << "] = " << arr[l[i]] 
             << endl;
    }
    return 0;
}

HZOJ-271 滑动窗口

int main() {
    int n, k;
    cin >> n >> k;
    vector<int> arr(n);
    deque<int> q;
    for (int i = 0; i < n; i++) {
        cin >> arr[i];
    }
    for (int i = 0; i < n; i++) {
        while (!q.empty() && arr[q.back()] > arr[i]) q.pop_back();
        q.push_back(i);
        if (i - q.front() == k) q.pop_front();
        if (i + 1 < k) continue;
        if (i >= k) cout << " ";
        cout << arr[q.front()];
    }
    cout << endl;
    q.clear();
    for (int i = 0; i < n; i++) {
        while (!q.empty() && arr[q.back()] < arr[i]) q.pop_back();
        q.push_back(i);
        if (i - q.front() == k) q.pop_front();
        if (i + 1 < k) continue;
        if (i >= k) cout << " ";
        cout << arr[q.front()];
    }
    cout << endl;
    return 0;
}

HZOJ-270 最大子序和

int main() {
    int n, m;
    cin >> n >> m;
    vector<int> s(n + 1);
    deque<int> q;
    s.push_back(0);
    for (int i = 1; i <= n; i++) {
        cin >> s[i];
        s[i] += s[i - 1];
    }
    q.push_back(0);
    int ans = s[1];
    for (int i = 1; i <= n; i++) {
        ans = max(ans, s[i] - s[q.front()]);
        while (!q.empty() && s[i] < s[q.back()]) q.pop_back();
        q.push_back(i);
        if (q.front() == i - m) q.pop_front();
    }
    cout << ans << endl;
    return 0;
}

Leetcode-LCR184 设计自助结算系统

class Checkout {
public:
    queue<int>q;
    deque<int>d;
    Checkout() {}

    int get_max() {
        if(d.empty())return -1;
        return d.front();
    }

    void add(int value) {
        while(!d.empty()&&d.back()<value){
            d.pop_back();
        }
        d.push_back(value);
        q.push(value);
    }

    int remove() {
        if(q.empty())return -1;
        int ans=q.front();
        q.pop();
        if(ans==d.front()){
            d.pop_front();
        }
        return ans;
    }
};

HZOJ-264 最大矩形面积

int main() {
    long long n;
    cin>>n;
    vector<long long>arr(n+2,-1), l(n + 2), r(n + 2);//两个哨兵
    for (long long i = 1; i <= n; i++) {
        cin>>arr[i];
    }
    stack<long long> s;
    for (long long i = 1; i <= n + 1; i++){
        while(!s.empty()&&arr[i]<arr[s.top()]){
            r[s.top()] = i;
            s.pop();
        }
        s.push(i);
    }
    while (!s.empty()) s.pop();
    for (long long i = n; i >= 0; i--) {
        while (!s.empty() && arr[i] < arr[s.top()]) {
            l[s.top()] = i;
            s.pop();
        }
        s.push(i);
    }
    long long ans = 0;
    for (long long i = 1; i <= n; i++){
        long long height=arr[i];
        long long width=r[i]-l[i]-1;
        ans=max(ans,height*width);
    }
    cout<<ans;
    return 0;
}

Leetcode-1438 绝对差不超过限制的最长连续子数组

变长的滑动窗口

class Solution {
public:
    int longestSubarray(vector<int>& nums, int limit) {
        deque<int> min_q, max_q;
        min_q.push_back(0);
        max_q.push_back(0);
        int n=nums.size();
        int l=0,ans=1;
        for (int r = 1; r < n; r++) {
            while (!min_q.empty() && nums[r] < nums[min_q.back()]) min_q.pop_back();
            while (!max_q.empty() && nums[r] > nums[max_q.back()]) max_q.pop_back();
            min_q.push_back(r);
            max_q.push_back(r);
            while (nums[max_q.front()] - nums[min_q.front()] > limit) {
                if (min_q.front() == l) min_q.pop_front();
                if (max_q.front() == l) max_q.pop_front();
                l += 1;
            }
            ans = max(ans, r - l + 1);
        }
        return ans;
    }
};

Leetcode-42 接雨水

单调栈维护单调递减的序列

class Solution {
public:
    int trap(vector<int>& height) {
        int ans=0;
        stack<int>s;
        for(int i=0;i<height.size();i++){
            while(!s.empty() && height[s.top()] < height[i]){
                int h=height[s.top()];
                s.pop();
                if(s.empty())break;
                ans+=(min(height[s.top()], height[i]) - h) * (i - s.top() - 1);
            }
            s.push(i);
        }
        return ans;
    }
};

Leetcode-862 和至少为 K 的最短子数组

维护单调递增单调队列

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

HZOJ-372 双生序列

int main() {
    int n;
    cin>>n;
    vector<int> a(n), b(n);
    for (int i = 0; i < n; i++) cin>>a[i];
    for (int i = 0; i < n; i++) cin>>b[i];
    deque<int>aq,bq;
    int i;
    for(i=0;i<n;i++){
        while(!aq.empty()&&a[i]<=aq.back())aq.pop_back();
        while(!bq.empty()&&b[i]<=bq.back())bq.pop_back();
        aq.push_back(a[i]);
        bq.push_back(b[i]);
        if (aq.size()!= bq.size()) break;
    }
    cout<<i;
    return 0;
}
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇