力扣 滑动窗口最大值

滑动窗口最大值

题目描述

在这里插入图片描述

题目分析

维护一个定长窗口的最大值,每当窗口滑动时都有一个新的元素进入和一个原有的元素离开。
比较简单的方法就是用一个优先队列维护窗口最大值
但是堆的计算成本时最坏时是 O ( n log ⁡ n ) O(n\log n) O(nlogn)

优化:

单调队列
相比于堆,堆保留了全部元素,导致维护的成本较高,而单调队列是在保持顺序的情况下只保留符合单调性的元素,不需要重新重新调整结构,换句话说计算的复杂度最坏时为 O ( n ) O(n) O(n)
分块预处理
将所有元素按滑动窗口的大小分块,我们可以得到 [ n / k ] + 1 [n/k] + 1 [n/k]+1个块
为了方便的得到块内最大值,我们可以参考前缀和的方式计算块内的前缀最大值,取 i i i k k k的倍数时就可以取得第 i / k i/k i/k块的最大值
问题在于,滑动窗口不一定就是我们分好的块
所以考虑特殊情况,滑动窗口为 [ i , j ] [i,j] [i,j],设 m ∈ [ i , j ] 且 k ∣ m m \in [i,j] 且 k\mid m m[i,j]km(整除)
p r e m a x [ j ] premax[j] premax[j]表示了 m a x ( n u m s [ m : j ] ) max(nums[m:j]) max(nums[m:j])
现在问题转为了怎么求 m a x ( n u m s [ i : m ) ) max(nums[i:m)) max(nums[i:m))
我们希望用 i i i表示这一段的最大值。注意到 i : m i:m i:m是一种后缀形式,相应的我们可考虑块内的后缀最大值
定义 s u f m a x [ i ] 表示了 m a x ( n u m s [ i : m ) ) sufmax[i]表示了max(nums[i:m)) sufmax[i]表示了max(nums[i:m))

解决问题

1.优先队列

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        int n = nums.size();
        priority_queue<pair<int, int>> q;
        for (int i = 0; i < k; ++i) {
            q.emplace(nums[i], i);
        }
        vector<int> ans = {q.top().first};
        for (int i = k; i < n; ++i) {
            q.emplace(nums[i], i);
            while (q.top().second <= i - k) {
                q.pop();
            }
            ans.push_back(q.top().first);
        }
        return ans;
    }
};

2.单调队列

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

3.分块预处理

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> prefixMax(n), suffixMax(n);
        for (int i = 0; i < n; ++i) {
            if (i % k == 0) {
                prefixMax[i] = nums[i];
            }
            else {
                prefixMax[i] = max(prefixMax[i - 1], nums[i]);
            }
        }
        for (int i = n - 1; i >= 0; --i) {
            if (i == n - 1 || (i + 1) % k == 0) {
                suffixMax[i] = nums[i];
            }
            else {
                suffixMax[i] = max(suffixMax[i + 1], nums[i]);
            }
        }

        vector<int> ans;
        for (int i = 0; i <= n - k; ++i) {
            ans.push_back(max(suffixMax[i], prefixMax[i + k - 1]));
        }
        return ans;
    }
};

总结

优先队列,单调队列,动态维护最值
分块预处理,空间换时间,从整体出发,问题分治,对称性思想

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值