239.滑动窗口最大值

一个和 滑动窗口有关的题目 官方给出了三种解法  很值得借鉴

方法一: priority_queue O(nlogn)

        使用模板库的优先队列保存pair(i, nums[i])  

        在取最大值 .top()  的时候   注意 看一看它的下标在不在范围内(<=i-k), 不在的时候需要把它(队首元素)pop出去   每次push或者pop的时间复杂度都是log n 

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

方法二: 性质破局

        注意到窗口是向右移动的  所以对于下标i,j (i<j)  当 i 和 j 都在窗口中时,如果nums[i] <= nums[j] ,那么显然这个时候就 nums[i]就不会是窗口的最大值(之后也不会)

        所以我们可以把nums[i] 删除 ,针对此题   我们可以维护一个单调队列

        显然从队头到队尾(从左到右) 数值应该是严格单调递减的 不然不满足上面的性质

        每次加入新元素的时候,如果比当前的队尾要小就直接加入 ,否则就把排在它前面但是数值比它还要小或者和它相等的元素一一删除,以此保证单调性 

        在每次取元素的时候, 取队头 同时保证这个元素的下标可以在当前的范围内也就是(<=n-k)

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        deque<int> dq;
        //单调队列   左边下标小的是头   右边下标大的是尾部
        //如果i<j  而且 nums[i] <= nums[j] 那么 i不会是最大值
        //所以可以把nums[i]去掉
        //所以随着下标从小到大  数值应该是从大到小
        //所以每次新来一个nums[k]都应该拿它和 队尾(最小的元素) 比较
        //如果新来的更小  ->  加入
        //如果新来的大    ->  把小于等于它的都删除   再加入
        for(int i=0;i<k;i++){
            while(!dq.empty() && nums[dq.back()] <= nums[i] ){
                dq.pop_back();
            }
            dq.push_back(i);
        }
        vector<int> ans={ nums[dq.front()] };
        for(int i=k;i<nums.size();i++){
            while(!dq.empty() && nums[dq.back()] <= nums[i] ){
                dq.pop_back();
            }
            dq.push_back(i);
            while(dq.front() <= i-k ){
                dq.pop_front();
            }
            ans.push_back( nums[dq.front()] );
        }
        return ans;
    }
};

方法三: 分组 + 前后缀

        还有一个有意思的做法就是分组   把元素k个一组分开 ,最后一组可能不足k个

        然后计算nums[i]这个元素在它所在的分组里面的最大前后缀,记为prefix[i] 和 suffix[i]

窗口和组的关系只用两种:

        1. 当前窗口刚好是一个分组 比较第一个元素的最大后缀 or 最后一个元素的最大前缀

        2.当前窗口跨越前后两个分组 计算前一个组最大后缀 和 后一个组最大前缀 进行比较        

归结起来都是 ans[i] = max(suffix[i] , prefix[i+k-1])

前后缀的计算如图:  这个如果对前后缀 有一点了解可以自己简单递推

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> prefix(nums.size()); //分组前缀中的max
        vector<int> suffix(nums.size()); //分组后缀中的max
        for(int i=0;i<nums.size();i++){
            if(i%k==0){
                prefix[i] = nums[i];
            }else{
                prefix[i] = max(prefix[i-1] , nums[i]);
            }
        }

        for(int i=nums.size()-1;i>=0;i--){
            if(i==nums.size()-1 || (i+1)%k == 0){
                suffix[i] = nums[i];
            }else{
                suffix[i] = max(nums[i] , suffix[i+1]);
            }
        }

        vector<int> ans(nums.size()+1-k);
        for(int i=0;i+k-1<nums.size();i++){
           ans[i] = max(suffix[i] , prefix[i+k-1]);
        }
        return ans;
    }
};

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值