剑指 Offer -- 滑动窗口的最大值(六十四)

滑动窗口的最大值(六十四)

题目描述:
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

代码(牛客上已 AC)

这道题呢, 难度是有的. 可以参阅 http://cuijiahua.com/blog/2018/02/basis_64.html 了解更详细的解释. 下面简单说下代码.

这道题需要用到一个双端队列, 本来队列中可以保存最大值, 但由于要判断某些数是否已经不在滑动窗口中了, 所以保存索引会更为方便. 下面代码中用到了一个 deque<int> index. 每当访问到一个新的元素 num[i], 如果它比队列中出现的元素都要大, 那么就要将队列中的元素都弹出来, 即当 num[i] >= num[index.back()] 时, 需要对 index 进行 pop_back() 操作, 直到队列中的元素都比 num[i] 大或者 index 为空, 才将 num[i] 插入到队列中(注意实际插入的是索引, 但为了叙述方便, 改用元素本身).

那么滑动窗口中的最大元素就是 num[index.front()] 了. 也就是说, index 的首端总是维护一个滑动窗口中最大值的索引. 下面代码先用一个 for 循环求出第一个滑动窗口中的最大值, 之后再移动窗口. 移动窗口的过程中, 就有可能使得 index.front() 超出滑动窗口的范围, 超出的条件为, 当前访问的元素 num[i] 的位置 iindex.front() 的距离大于或等于 size (下面 if 判断语句中 index.front() <= int(i - size) 相当于判断 i - index.front() >= size), 此时就需要把 index.front() 先弹出, 再将 i 插入到 index 中.

class Solution {
public:
    // 使用一个 deque 来保存可能为最大值的索引, deque 的 front() 保存当前窗口的最大值的索引.
    // 如果新进来的 num[i] 的索引满足 i - deque.front() >= size, 说明 front() 已经超出
    // 当前窗口的范围, 可以丢弃.
    vector<int> maxInWindows(const vector<int>& num, unsigned int size) {
        if (size < 1 || size > num.size()) return {};
        deque<int> index;
        vector<int> maxInWindow;
        for (int i = 0; i < size; ++i) {
            // 如果 num[i] 总是大于或等于 index 中 back() 的元素,
            // 那么就将它们给弹出
            while (!index.empty() && num[i] >= num[index.back()])
                index.pop_back();
            index.push_back(i);
        }
        maxInWindow.push_back(num[index.front()]);
        for (int i = size; i < num.size(); ++i) {
            while (!index.empty() && num[i] >= num[index.back()])
                index.pop_back();
            // 当前访问的元素的索引与deque 中的 front() 距离大于或等于 size, 说明
            // 最大值要更新了; 注意这里是 pop_front();
            if (!index.empty() && index.front() <= int(i - size))
                index.pop_front();
            index.push_back(i);
            maxInWindow.push_back(num[index.front()]);
        }
        return maxInWindow;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值