题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}。
解题思路
题目本身很容易理解,最核心的难点在于怎样维护这个滑动窗口的数据结构,使之可以快速更新并且找到最大值。本题使用了一个std::multiset
数据结构,该结构用来维护窗口。
先明确几个数据结构的复杂度:
堆插入和删除的复杂度是 log 2 N \log_2^{N} log2N,但是如果在一个无序的结构上建堆,复杂度是 O ( n ) O(n) O(n)。本题如果每次都建堆,那么和暴力法没区别,复杂度都是 O ( m ∗ ( n − m ) ) O(m*(n - m)) O(m∗(n−m)),其中 m m m是滑动窗口的长度, n n n是元素的个数。
红黑树查找、插入、删除的复杂度是
O
(
log
2
N
)
O(\log_2{N})
O(log2N),本题也是利用了这个结构进行求解。先在第一个窗口建立一个std::muitlset
,因为有重复的值,所以不能用std::set
。然后,每次更新去掉窗口的前一个元素,又因为std::muitlset
是有序的结构,最后一个元素是最大的,所以每次取出都是
O
(
log
2
N
)
O(\log_2{N})
O(log2N)。综上可知,该方法的复杂度是
O
(
(
log
2
m
)
∗
(
n
−
m
)
)
O((\log_2{m})*(n - m))
O((log2m)∗(n−m))
注意边界条件的处理,坑死劳资了。。。。
AC代码
class Solution {
public:
vector<int> maxInWindows (const vector<int>& num, unsigned int size) {
int N = num.size () - size;
vector<int> res;
if (num.empty() || N < 0 || size < 1) { // 别忘了size的边界条件。。。
return res;
}
multiset<int> numbers;
for (unsigned int i = 0; i < size; ++i) {
numbers.insert (num[i]);
}
res.push_back (*prev(numbers.end()));
N = num.size ();
for (unsigned int i = size; i < N; ++i) {
auto it = numbers.find (num[i - size]);
numbers.erase (it);
numbers.insert (num[i]);
res.push_back (*prev(numbers.end()));
}
return res;
}
};