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)
//滑動視窗的大小(k),隊列彈出隊列頭元素以維持滑動視窗的大小
- 當隊列不為空(hh <= tt) 且 當目前滑動視窗的大小(i - q[hh] + 1)>我們設定的
- 構造單調遞增隊列
//當隊列不為空(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();
}
}