LeetCode 239 Sliding Window Maximum

LeetCode 239 Sliding Window Maximum

思路

相当有趣的一道题,除了暴力外有三种解法

有序集合

使用multiset,初始化前k个数,然后每次查找-删除-添加-取最大值即可。C++里set multiset是有序的,虽然不支持随机访问,但是指定greater模板参数后就可以直接使用*set.begin()获得最大值了。

双端队列

维护一个双端队列,该队列总是保持如下特性

  1. 队列中任一位置前的元素总比该位置的大
  2. 队列最前端元素是最大值
  3. 队列最大为k

上述特性能够保证在O(1)的时间内获得最大值,为保持这些特性,考察一个新元素时,需要如下操作

  1. 若队列长度大于k,则不断将队列前端出队直到长度为k
  2. 从队列尾部开始,若尾部元素小于等于当前元素a[i]则出队,直到遇到大于a[i]的或队列为空
  3. a[i]入队

然后就可以每次获得最大值了

分块

将数据分块,每块k个元素,那么对于某个滑动窗口x, x+k-1,该窗口最多只覆盖两个块。因此,只需求出每个块内,前缀最大值后缀最大值,那么对于一个覆盖两个块的窗口x, x+k-1,假设其覆盖的块为[x, y][y+1, x+k-1],则该窗口的最大值是 max ( max(x~y), max(y+1, x+k-1) ),即前一个块的后缀最大值和后一个块的前缀最大值

代码

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int> &nums, int k) {
        // 分块
        if (k == 0 || nums.empty() || nums.size() < k)
            return {};
        const int sz = nums.size();
        vector<int> res(sz - k + 1, 0), lKMax(sz, 0), rKMax(sz, 0);
        for (int i = 0; i < sz; i++) {
            if (i % k == 0)
                lKMax[i] = nums[i];
            else
                lKMax[i] = max(lKMax[i - 1], nums[i]);
        }
        rKMax[sz - 1] = nums[sz - 1];
        for (int i = sz - 2; i >= 0; i--) {
            if (i % k == 0)
                rKMax[i] = nums[i];
            else
                rKMax[i] = max(rKMax[i + 1], nums[i]);
        }
        for (int i = 0; i < sz - k + 1; i++) {
            res[i] = max(rKMax[i], lKMax[i + k - 1]);
        }
        return res;
    }
    vector<int> maxSlidingWindow1(vector<int> &nums, int k) {
        // 队列法
        if (k == 0 || nums.empty() || nums.size() < k)
            return {};
        const int sz = nums.size();
        vector<int> res;
        res.reserve(sz - k + 1);
        deque<int> q;
        for (int i = 0; i < sz; i++) {
            while (!q.empty() && q.front() < i - k + 1)
                q.pop_front();
            while (!q.empty() && nums[q.back()] < nums[i])
                q.pop_back();
            q.push_back(i);
            if (i >= k - 1)
                res.push_back(nums[q.front()]);
        }
        return res;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值