问题描述
小蓝有一个序列 a[1],a[2],…,a[n]。
给定一个正整数 k,请问对于每一个 1 到 n 之间的序号 i,a[i−k],a[i−k+1],…,a[i+k] 这2k+1 个数中的最小值是多少?
当某个下标超过 1 到 n 的范围时,数不存在,求最小值时只取存在的那些值。
输入格式
输入的第一行包含一整数 n。
第二行包含 n 个整数,分别表示 a[1],a[2],…,a[n]。
第三行包含一个整数 k 。
输出格式
输出一行,包含 n 个整数,分别表示对于每个序号求得的最小值。
样例输入
5
5 2 7 4 31
样例输出
2 2 2 3 3
评测用例规模与约定
对于 30% 的评测用例,1<=n<=1000,1<=a[i]<=1000。
对于 50% 的评测用例,1<=n<=10000,1<=a[i]<=10000。
对于所有评测用例,1<=n<=1000000,1<=a[i]<=1000000。
运行限制
语言 | 最大运行时间 | 最大运行内存 |
---|---|---|
C++ | 1s | 256M |
C | 1s | 256M |
Java | 3s | 256M |
Python3 | 12s | 512M |
PyPy3 | 12s | 512M |
Go | 12s | 512M |
JavaScript | 12s | 512M |
一、ST表
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int len = (int) (Math.log(n)/Math.log(2));
int[][] st = new int[n+1][len+1];
for (int i = 1; i <= n; i++) {
st[i][0] = scan.nextInt();
}
for (int j = 1; j <= len; j++) {
for (int i = 1; i + (1<<j) - 1 <= n; i++) {
st[i][j] = Math.min(st[i][j-1], st[i+(1<<j-1)][j-1]);
}
}
int k = scan.nextInt();
for (int t = 1; t <= n; t++) {
int start = Math.max(1, t-k);
int end = Math.min(n, t+k);
int j = (int) (Math.log(end-start+1)/Math.log(2));
System.out.print(Math.min(st[start][j], st[end-(1<<j)+1][j])+" ");
}
}
}
二、线段树
参考文章:超详解线段树(浅显易懂,几乎涵盖所有线段树类型讲解,匠心之作,图文并茂)-CSDN博客
import java.util.*;
public class Main {
static int[] tree,a;
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
tree = new int[4*n+1];
a = new int[n+1];
for (int i = 1; i <= n; i++) {
a[i] = scan.nextInt();
}
BuildTree(1, 1, n);
int k = scan.nextInt();
for (int i = 1; i <= n; i++) {
int x = Math.max(i-k, 1);
int y = Math.min(i+k, n);
System.out.print(find(1, 1, n, x, y)+" ");
}
}
public static void BuildTree(int id,int l,int r) {
if (l == r) {
tree[id] = a[l];
return;
}
int mid = (l+r)/2;
BuildTree(id*2, l, mid);
BuildTree(id*2+1, mid+1, r);
tree[id] = Math.min(tree[id*2], tree[id*2+1]);
}
public static int find(int id,int l,int r,int x,int y) {
if (x <= l && y >= r) {
return tree[id];
}
int mid = (l+r)/2;
int res = Integer.MAX_VALUE;
if (x <= mid) {
res = Math.min(res, find(id*2, l, mid, x, y));
}
if (y > mid) {
res = Math.min(res, find(id*2+1, mid+1, r, x, y));
}
return res;
}
}
三、单调队列
参考文章:单调队列的学习 - 滑动窗口求最大/小值 (Leetcode 239, Sliding Window Maximum)_滑动窗口最小值-CSDN博客
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int[] min = new int[n+1];
int[] a = new int[n+1];
for (int i = 1; i <= n; i++) {
a[i] = scan.nextInt();
}
int k = scan.nextInt();
LinkedList<Integer> list = new LinkedList<Integer>();
for (int i = 1; i <= k+1; i++) {
while (!list.isEmpty() && i <= n && a[list.peekLast()] >= a[i]) {
list.pollLast();
}
list.addLast(i);
}
min[1] = a[list.peekFirst()];
for (int i = 2; i <= n; i++) {
if (!list.isEmpty() && i-k > 0 && list.peekFirst() < i-k) {
list.pollFirst();
}
while (!list.isEmpty() && i+k <= n && a[list.peekLast()] >= a[i+k]) {
list.pollLast();
}
if (i+k <= n) {
list.addLast(i+k);
}
min[i] = a[list.peekFirst()];
}
for (int i = 1; i <= n; i++) {
System.out.print(min[i]+" ");
}
}
}