【剑指 Offer 59 - I. 滑动窗口的最大值 简单】【239. 滑动窗口最大值 困难】【剑指 Offer 59 - II. 队列的最大值 中等】

【剑指 Offer 59 - I. 滑动窗口的最大值 简单】【239. 滑动窗口最大值 困难】

该题是一提,难度至少应该是中等

题目描述

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回滑动窗口中的最大值。

 

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置 最大值
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
示例 2:

输入:nums = [1], k = 1
输出:[1]
示例 3:

输入:nums = [1,-1], k = 1
输出:[1,-1]
示例 4:

输入:nums = [9,11], k = 2
输出:[11]
示例 5:

输入:nums = [4,-2], k = 2
输出:[4]

2. 思路一

利用一个大顶堆可以随时获得堆内元素最大值,随着窗口滑动,会有新的元素进入堆,很容易获得最大值。但是需要注意的是:窗口滑动的过程中,也会有元素从窗口内离开,该元素如果是不是堆顶元素,则可任其放置在堆中,因为不影响获取当前的最大值;如果该元素是堆顶元素,则需将该元素从堆内删除,重新获得堆顶元素,如果堆顶元素还是已经被从窗口移除的元素,应该持续循环删除。那么拿到堆顶元素如何判断它是不是还在窗口内呢?只需需将(元素值,元素索引)同时存入堆即可。

代码

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        int len = nums.size();
        vector<int> res;
        if(len == 0 || k <= 0)
            return res;
        priority_queue<pair<int, int>> q;
        for(int i = 0; i < k; i++) {
            q.emplace(nums[i], i); // 原地构造一个元素并插入队列
        }
        res.push_back(q.top().first);
        for(int i = k; i < len; i++) {
            q.emplace(nums[i], i);
            while(q.top().second < i - k + 1) {
                q.pop();
            }
            res.push_back(q.top().first);
        }
        return res;
    }
};

关于C++优先队列的用法,可参考:https://blog.csdn.net/weixin_36888577/article/details/79937886

该解法的最坏时间复杂度为o(n* log(n)),因为如果数组是个递增数组,那么堆的大小也会递增。

解法二:
使用单调队列,思路可参考LeetCode官方题解:

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

 

 

【剑指 Offer 59 - II. 队列的最大值 中等】

1. 题目描述:

请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。

若队列为空,pop_front 和 max_value 需要返回 -1

示例 1:

输入:
["MaxQueue","push_back","push_back","max_value","pop_front","max_value"]
[[],[1],[2],[],[],[]]
输出: [null,null,null,2,1,2]
示例 2:

输入:
["MaxQueue","pop_front","max_value"]
[[],[],[]]
输出: [null,-1,-1]

2. 思路

借助一个辅助单调队列来记录最大值。队的front始终存储当前队列的最大值。向队列中push时,取辅助队列的队尾元素,如果队尾元素小于当前要push的元素,则将队尾元素从辅助队列中删除,因为当前队尾元素先于当前要push的元素入队(出队时先出队),那么如果当前队尾元素小于要push的元素,那么队列的最大值永远不可能是当前队尾元素

代码:

class MaxQueue {
public:
    queue<int> q;
    deque<int> auxQueue;
    MaxQueue() {
        auxQueue.clear();
    }
    
    int max_value() {
        if(auxQueue.empty())
            return -1;
        return auxQueue.front();
    }
    
    void push_back(int value) {
        while(!auxQueue.empty() && auxQueue.back() < value) {
            auxQueue.pop_back();
        }
        auxQueue.push_back(value);
        q.push(value);
    }
    
    int pop_front() {
        if(q.empty())
            return -1;
        int res = q.front();
        if(res == auxQueue.front()) {            
            auxQueue.pop_front();
        }
        q.pop();
        return res;
    }
};

/**
 * Your MaxQueue object will be instantiated and called as such:
 * MaxQueue* obj = new MaxQueue();
 * int param_1 = obj->max_value();
 * obj->push_back(value);
 * int param_3 = obj->pop_front();
 */

 

posted @ 2021-03-14 16:27  蓦然闻声  阅读(30)  评论(0编辑  收藏  举报