定义一下窗口的概念:
![ec006e6de58d3c85a2e34452673217c0.png](https://i-blog.csdnimg.cn/blog_migrate/fba0da0895bcc3cdef4025ce63b8a045.png)
![c889d1fa271cca76506f89ad11e6da13.png](https://i-blog.csdnimg.cn/blog_migrate/7cf7c976bcd9eb0a968769db6cd10b85.png)
![3a4e1b94b06ff3a1eb05ebf25b63e9bd.png](https://i-blog.csdnimg.cn/blog_migrate/c0bff59cd962549d395f312a0a3ba6ca.png)
![4b8206b80806a8a0127ebcf3b41ec034.png](https://i-blog.csdnimg.cn/blog_migrate/0287e9bca677a688426292b0fc3994eb.png)
R往右动的时候,将有num从右进来。
l往右动的时候,num从左侧出来。
L<R。
窗口内最大值和最小值的更新结构:
功能:
1.告诉我窗口内的最大值。
2.维持窗口内的最大值更新。
复杂度为O(1)
双指针确定一个窗口
![bcd1afc7b9856e92b5c4c88003f2fcda.png](https://i-blog.csdnimg.cn/blog_migrate/754df1fc9f3e5cb99d6f619a18e9e1b8.png)
只有两种移动方式。
1.L右移
2.R右移。
也即只有一种移动方向,向右移动。
同时,生成一个双端队列。deque
![ed80d928e75a3c0553c01bfed204d4f6.png](https://i-blog.csdnimg.cn/blog_migrate/91e83a5fc43249bd945d7ae97515f972.png)
保证这个双端队列从头到尾按照由大到小的顺序排列。
考虑R右移的情况:右移只能从尾部进入
第一个数字,R右移得到,直接进入。
![8d2d2e9ebdc2869e00d53b665a8a19e7.png](https://i-blog.csdnimg.cn/blog_migrate/2d8aedc7273b0315121d65fd27912aa3.jpeg)
当进入第二个数字5时,发现5大于3,此时,将3pop出来,将5放入。
![134564dc49d87273162bb7ded19e4092.png](https://i-blog.csdnimg.cn/blog_migrate/abe1a510fffa5b34af5440ac7636d1fb.jpeg)
2可以直接进入。
![6fb1007e08d9e49898715e6d5f31333d.png](https://i-blog.csdnimg.cn/blog_migrate/ed5b666ac44b988312178d8ab59364fd.png)
4不能直接进入。
将2弹出,发现符合排序,进入4
![f7c056ba77bc57e18d301281aa02cdff.png](https://i-blog.csdnimg.cn/blog_migrate/5d7e6193d54fb2e745f1ce393cbaf07c.png)
可以发现,当R不断右移,窗口不断增大,窗口内最大值就是双端队列头部的元素。
考虑L动的情况:
L右移,那么就用一个元素不在窗口内,此时看看双端队列中,头部元素是不是这个离开的元素,如果是,将它弹出。
这是为什么呢?
这个双端队列代表什么?
成为最大值的可能性。
开始时,
![db7af610dfdc262de98c70b377e37954.png](https://i-blog.csdnimg.cn/blog_migrate/8a958d10687129af1db69ab554a4ec65.png)
进入3,代表着,窗口L-R中的最大值就是3,因为窗口内就一个元素。
然后进入5,为啥要将3弹出呢?
因为5已经是这个窗口内的最大值了,并且,5在3之后出现,那么当L继续右移(L,R都只能右移),3一定会先离开双端队列,5后离开。无论如何,3都无法成为最大值。
然后再加入2,此时,不弹出5.因为2小于5。
这是为什么?
因为2虽然比5小,在当前窗口内无法成为最大值,但是当L右移之后,5离开了。2有成为最大值的潜力。
如果出现两个数字相同的元素进入。同样抛弃之前的元素,因为虽然数字相同,但是后面的数字晚离开。
既然你会了滑动窗口的最大值,那么滑动窗口的最小值怎么设计呢?
1.deque是从小到大顺序。
2.R右移,发现当前数字小于deque中数字,pop出来。
3.>时,将其加入
4.L右移时,pop top
下面是窗口内最大值的代码:
#include <iostream>
#include <vector>
#include <deque>
using namespace std;
vector<int> getWindowsMax(const vector<int> num, const int m) {
vector<int> res;
deque<int> index;//双端队列,按照头到尾由大到小的顺序排列。储存num中数字的下标。
/*if (num.empty() || m == 0) return res;*/
for (int l = 0, r = 0; r < num.size(); ++r) {
if (r - l >= m) {
//窗口极限 此时要移动L右移。
if (index[0] == l) {
//如果l的位置是双端队列中的头部元素,那么就要将头部元素pop出来
index.pop_front();
}
++l;//L右移。
}
while (!index.empty() && num[r] >= num[index.back()]) index.pop_back();
index.push_back(r);
if (r - l == m - 1) //构成了一个窗口
//就求这个窗口内的最大值。
res.push_back(num[index.front()]);
}
return res;
}
void test() {
vector<int> v = { 4,3,5,4,3,3,6,7 };
vector<int> res = getWindowsMax(v, 3);
for (auto i:res) {
cout << i << " ";
}
}
int main() {
test();
return 0;
}
同理也能写出窗口内最小值的代码:
#include <iostream>
#include <vector>
#include <deque>
using namespace std;
vector<int> getWindowsMin(const vector<int> num, const int m) {
vector<int> res;
deque<int> index;//双端队列,按照头到尾由大到小的顺序排列。储存num中数字的下标。
/*if (num.empty() || m == 0) return res;*/
for (int l = 0, r = 0; r < num.size(); ++r) {
if (r - l >= m) {
//窗口极限 此时要移动L右移。
if (index[0] == l) {
//如果l的位置是双端队列中的头部元素,那么就要将头部元素pop出来
index.pop_front();
}
++l;//L右移。
}
while (!index.empty() && num[r] <= num[index.back()]) index.pop_back();
index.push_back(r);
if (r - l == m - 1) //构成了一个窗口
//就求这个窗口内的最大值。
res.push_back(num[index.front()]);
}
return res;
}
void test() {
vector<int> v = { 4,3,5,4,3,3,6,7 };
vector<int> res = getWindowsMin(v, 3);
for (auto i:res) {
cout << i << " ";
}
}
int main() {
test();
return 0;
}
下面来看一道题:
给定一个数组,和一个数num,求满足这个数组子数组中max-min<=num的个数。
先来看看性质:
子数组个数有n^2个。
如果一个数组满足max-min<=num,那么这个数组中的所有子数组也满足这个性质。
因为,在子数组中,找到的max只能更小,min只能更大。
子数组不满足了,大数组也不满足,
因为大数组中的max 会更大,min会更小。
因此,从第一个元素开始,0位置,一直向外,求滑动窗口内的最大值和最小值。
判断是否满足,如果满足,继续进行。直至不满足。此时,r的位置+1就是满足性质的个数。
接下来从第二个位置开始。重复进行。
int windowMaxMin(vector<int> nums,int num) {
cout<<"in function"<<endl;
deque<int> maxIndex;
deque<int> minIndex;
int r = 0;
int l=0;
int cnt = 0;
while (l < nums.size()) {
cout<<"in loop"<<endl;
while (r < nums.size()) {
while (!maxIndex.empty() && nums[r] >= nums[maxIndex.back()]) {
maxIndex.pop_back();
}
maxIndex.push_back(r);
while (nums[r] <= nums[minIndex.back()]&& !minIndex.empty()) {
minIndex.pop_back();
}
minIndex.push_back(r);
if (maxIndex.front() - minIndex.front() > num) break;
r++;
}
if (maxIndex[0] == l) maxIndex.pop_front();
if (minIndex[0] == l) minIndex.pop_front();
cnt += r-l;
l++;
cout<<cnt<<" ";
}
return cnt;
}