滑动窗口的最大值(六十四)
题目描述:
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]
的位置 i
与 index.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;
}
};