每日一练:滑动窗口最大值

239. 滑动窗口最大值 - 力扣(LeetCode)

一、题目要求

给你一个整数数组 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]

提示:

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104
  • 1 <= k <= nums.length

二、解法1-暴力破解 O(N^2) 会超时

遍历数组,得到每一次滑动窗口的最大值

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> ret;
        for(int i = 0;i+k <= nums.size();i++)
        {
            int max = nums[i];
            for(int j = 1;j < k;j++)
            {
                if(nums[i+j] > max)
                    max = nums[i+j];
            }
            ret.push_back(max);
        }
        return ret;
    }
};

三、解法2-更新最大值 O(N) 执行时间长、内存消耗小

用一个 max 保存当前窗口的最大值的下标,然后滑动窗口。

当一个更大或者相等的值到来时,更新max;

当新值小于最大值,但是最大值又失去时,重新计算max;

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> ret;
        int max = 0; // 滑动窗口当前的最大值的下标
        for (int i = 1; i < k; i++) {
            if (nums[i] >= nums[max])
                max = i;
        }
        ret.push_back(nums[max]);
        for (int i = 0; i + k < nums.size(); i++) {
            // 滑动窗口开始滑动
            if (nums[i + k] >= nums[max]) // 来了个更大的
            {
                max = i + k;
            } else // 没有来更大的
            {
                if (i == max) // 最大的没了,重新计算最大的
                {
                    max = i+1;
                    for (int j = i+1; j <= i+k; j++) {
                        if (nums[j] >= nums[max])
                            max = j;
                    }
                }
            }
            ret.push_back(nums[max]); // 获得最大值
        }
        return ret;
    }
};

四、解法3-优先级队列(大堆)O(N) 执行时间略短,内存消耗高

        用优先级队列维护最大值(根),窗口每次移动插入新值,然后把有影响的窗口外的值删除(大于当前窗口最大值,又在窗口外的数) 

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> ret;
        priority_queue<pair<int, int>> q;
        for(int i = 0;i < k;i++)
            q.emplace(nums[i],i);
        ret.push_back(q.top().first);
        for(int i = 0;i+k < nums.size();i++)
        {
            // 窗口开始滑动
            q.emplace(nums[i+k],i+k); // 获得新值
            while(q.top().second <= i) // 删除在窗口外,但是比窗口的最大值还大的值
            {
                q.pop(); // 删除根(最大值)
            } 
            ret.push_back(q.top().first);
        }
        return ret;
    }
};

五、解法4-单调队列 O(N) 执行时间短,空间消耗一般

        维护一个单调递增队列,窗口移动时,对于将进入窗口的值,如果比目前的队尾的值大,就永久弹出队尾元素,因为将插入的值肯定比队尾的值后离开窗口;

        然后排除已经不在窗口的队头的元素,因为维护的是一个单调递增队列,队头肯定是最大的,将队头对应的值插入结果即可;

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> ret;
        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);
        }
        ret.push_back(nums[q.front()]);
        for (int i = k; i < n; i++) {
            while (nums[i] >= nums[q.back()]) {
                q.pop_back();
                if (q.empty()) {
                    break;
                }
            }
            q.push_back(i);
            while(q.front() <= i-k)
            {
                q.pop_front();
            }
            ret.push_back(nums[q.front()]);
        }
        return ret;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值