算法与数据结构:滑动窗口
(第一次发CSDN,有不足之处望轻喷)
题目链接:154. 滑动窗口 - AcWing题库
题目分析:
题目的讲解我只是从最小值的方面去做讲解,最大值可以最小值思路是一样的。
得到这个题目首先我们先用常规的思路去思考一下。我们要求的是在k个数中的最值,首先我们就每次在给定数组中选k个数,然后遍历一次,每次输出最值,然后把第一个去掉,再在后面加上一个新进来的就可以了,下面通过演示来看一下:
例子用的就是题目给出的样例:
首先我们选中前3个数字,然后遍历一遍可以得出最小值是-1;
然后我们整体向后移一位,遍历得出最小值是-3;
重复我们的这个操作:
通过暴力可以得出最后的结果是-1 -3 -3 -3 3 3。
代码如下:
#include <iostream>
#include <cstdlib>
using namespace std;
const int Max = 1e6 + 5;
int a[Max];
int b[Max];
int main(int argc, char const *argv[])
{
int n, k;
int l = 0, r = 0;
cin >> n >> k;
for (int i = 0; i < n; i++)
cin >> a[i];
for (int i = 0; i < n; i++)
{
int min = 99999999;
if (l <= r && l < i - k + 1)
l++;
b[r++] = a[i];
if (i > k - 2)
{
for (int j = l; j < r; j++)
min = min < b[j] ? min : b[j];
cout << min << " ";
}
}
system("pause");
return 0;
}
上面是暴力的做法,因为这个题目要给出的数据范围是10^6,所以这个算法是肯定不能全过的。
下面我们来做进一下的优化,循环整个数组我们是肯定不能再向下优化了的,我们考虑去优化我们找最值的时候能不能有更快的方法呢?
首先我们看一下第一组数据1 3 -1,当你看到-1的时候前面的数据还有用吗,如果我们保证每次队列的最前面的数据都当前范围最小的,每次我们找最小的时候不就可以缩短成一个O(1)
的时间复杂度吗?这里我们就引进了单调队列的思想。
单调队列分为了单调递增和单调递减,可以分为用来求我们的这个题目,而单调队列就是保证队头一定是最值。比如1 3 -1,当有-1的时候就把前面的两个数删除,这样输出队首就是最小值了,这时滑到-3,-3<-1,把-1出队,-3入队,输出队首还是最小值。下面是代码:
#include <iostream>
using namespace std;
const int Max = 1e6 + 5;
int num[Max]; //原数组
int b[Max];
int main()
{
int n, k;
int l = 0, r = -1;
cin >> n >> k;
for (int i = 0; i < n; i++)
{
cin >> num[i];
}
//最小值
for (int i = 0; i < n; i++)
{
if (l <= r && b[l] < i - k + 1) //弹出第一个元素
l++;
while (l <= r && num[b[r]] >= num[i]) //维护单调队列
r--;
b[++r] = i;
if (i > k - 2) //输出
cout << num[b[l]] << " ";
}
cout << endl;
//最大值
l = 0, r = -1;
for (int i = 0; i < n; i++)
{
if (l <= r && b[l] < i - k + 1)
l++;
while (l <= r && num[b[r]] <= num[i])
r--;
b[++r] = i;
if (i > k - 2)
cout << num[b[l]] << " ";
}
return 0;
}
第一次写,有不足之处望指正。