1、题目描述
滑动窗口
给定一个大小为n≤106n≤106的数组。
有一个大小为k的滑动窗口,它从数组的最左边移动到最右边。
您只能在窗口中看到k个数字。
每次滑动窗口向右移动一个位置。
以下是一个例子:
该数组为[1 3 -1 -3 5 3 6 7],k为3。
窗口位置 最小值 最大值 [1 3 -1] -3 5 3 6 7 -1 3 1 [3 -1 -3] 5 3 6 7 -3 3 1 3 [-1 -3 5] 3 6 7 -3 5 1 3 -1 [-3 5 3] 6 7 -3 5 1 3 -1 -3 [5 3 6] 7 3 6 1 3 -1 -3 5 [3 6 7] 3 7 您的任务是确定滑动窗口位于每个位置时,窗口中的最大值和最小值。
输入格式
输入包含两行。
第一行包含两个整数n和k,分别代表数组长度和滑动窗口的长度。
第二行有n个整数,代表数组的具体数值。
同行数据之间用空格隔开。
输出格式
输出包含两个。
第一行输出,从左至右,每个位置滑动窗口中的最小值。
第二行输出,从左至右,每个位置滑动窗口中的最大值。
输入样例:
8 3 1 3 -1 -3 5 3 6 7
输出样例:
-1 -3 -3 -3 3 3 3 3 5 5 6 7
2、分析
(1)队列
使用数组模拟的队列
(2)
- 当队列不为空(hh <= tt) 且 当当前滑动窗口的大小(i - q[hh] + 1)>我们设定的
//滑动窗口的大小(k),队列弹出队列头元素以维持滑动窗口的大小
- 构造单调递增队列
//当队列不为空(hh <= tt) 且 当队列队尾元素>=当前元素(a[i])时,那么队尾元素
//就一定不是当前窗口最小值,删去队尾元素,加入当前元素(q[ ++ tt] = i)
3、代码
(1)先求滑动窗口的最小值
package cn.acwing.数据结构;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class 滑动窗口 {
static int N = 1000010;
static int[] q; //队列,数组中存储的是队列中元素的下标
static int hh = 0,tt = -1; //分别表示队列头、队尾
static int[] nums;
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] split = br.readLine().split(" ");
int n = Integer.parseInt(split[0]);
int k = Integer.parseInt(split[1]);
q = new int[n];
nums = new int[n];
String[] line = br.readLine().split(" ");
for(int i = 0;i < n;i ++){
nums[i] = Integer.parseInt(line[i]);
}
/**
* 滑动窗口中的最小值
*/
for(int i = 0;i < n;i ++){
//判断队头是否需要出队:hh <= tt表示队列非空
if(hh <= tt && i - q[hh] + 1 > k){
hh ++;
}
//比较当前元素nums[i]与队尾元素的大小,是否删除队尾元素
while(hh <= tt && nums[q[tt]] >= nums[i]){
tt --;
}
q[++ tt] = i;
if(i - k +1 >= 0){
System.out.print(nums[hh] + " ");
}
}
br.close();
}
}
(2)
注意:普遍Scanner可能会超时,所以使用BufferedReader效率更高。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
static int N = 1000010;
static int[] q; //队列,数组中存储的是队列中元素的下标
static int hh = 0,tt = -1; //分别表示队列头、队尾
static int[] nums;
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String[] split = br.readLine().split(" ");
int n = Integer.parseInt(split[0]);
int k = Integer.parseInt(split[1]);
q = new int[n];
nums = new int[n];
String[] line = br.readLine().split(" ");
for(int i = 0;i < n;i ++){
nums[i] = Integer.parseInt(line[i]);
}
/**
* 滑动窗口中的最小值
*/
for(int i = 0;i < n;i ++){
//判断队头是否需要出队:hh <= tt表示队列非空
if(hh <= tt && i - q[hh] + 1 > k){
hh ++;
}
//比较当前元素nums[i]与队尾元素的大小,是否删除队尾元素
while(hh <= tt && nums[q[tt]] >= nums[i]){
tt --;
}
q[++ tt] = i;
if(i - k +1 >= 0){
System.out.print(nums[q[hh]] + " ");
}
}
System.out.println();
hh = 0;
tt = -1;
/**
* 滑动窗口中的最大值
*/
for(int i = 0;i < n;i ++){
//判断队头是否需要出队:hh <= tt表示队列非空
if(hh <= tt && i - q[hh] + 1 > k){
hh ++;
}
//比较当前元素nums[i]与队尾元素的大小,是否删除队尾元素
while(hh <= tt && nums[q[tt]] <= nums[i]){
tt --;
}
q[++ tt] = i;
if(i - k +1 >= 0){
System.out.print(nums[q[hh]] + " ");
}
}
br.close();
}
}