单调队列 Java读取输入使用BufferedReader AC
思路:
暴力枚举的时间复杂度是O(nk)
单调队列–求滑动窗口的最小值–时间复杂度O(n)
维护一个单调上升的区间,队头始终是当前有效区间的最小值
当待插入元素的值小于等于队尾元素时,那么将队尾元素弹出,因为只要包含有待插入元素的区间,那么当前的队尾元素不可能是区间最小值,所以需要弹出。
当待插入元素的值大于队尾元素时,直接插入队尾,因为待插入的值可能是后面区间的最小值,假设后面的元素都比待插入元素大。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;
public class Main {
int hh = 0, tt = -1; // hh表示队头,tt表示队尾,初始化 hh > tt 表示队列中无元素
int N = 1000000 + 10; // 表示最大的队列元素
int[] q = new int[N]; // 数组模拟队列,队列中存储的是原数组a的下标
public static void main(String[] args) {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
Main ins = new Main();
try {
ins.solve(br);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void solve(BufferedReader br) throws IOException {
String[] s1 = br.readLine().split(" ");
int n = Integer.parseInt(s1[0]);
int k = Integer.parseInt(s1[1]);
int[] a = new int[n];
String[] s2 = br.readLine().split(" ");
for (int i = 0; i < n; i++) a[i] = Integer.parseInt(s2[i]);
for (int i = 0; i < n; i++) {
if (i - k + 1 > q[hh]) hh++; // 表示当前的hh在新的长度为k的区间内已过期,需要hh++
while (hh <= tt && a[i] <= a[q[tt]]) tt--; // 当前队尾元素大于等于第i个元素,需出队
q[++tt] = i;
if (i + 1 >= k) System.out.print(a[q[hh]] + " ");
}
System.out.println();
hh = 0; tt = -1;
for (int i = 0; i < n; i++) {
if (i - k + 1 > q[hh]) hh++; // 表示当前的hh在新的长度为k的区间内已过期,需要hh++
while (hh <= tt && a[i] >= a[q[tt]]) tt--; // 当前队尾元素小于等于第i个元素,需出队
q[++tt] = i;
if (i + 1 >= k) System.out.print(a[q[hh]] + " ");
}
}
}