题目描述
-
计算生成窗口最大值数组
-
有一个整型数组arr和一个大小为w的窗口从数组最左边滑到最右边。
-
现在要求实现一个函数,输入整型数组arr和窗口长度w,输出一个最大值数组res记录每个窗口的最大值。
解题方法1
- 最简单粗暴的方法就是直接两层遍历,代码如下。但是时间复杂度为 n*w 显然有优化的空间。
public class Test {
public static void main(String[] args) {
int [] arr = {4,3,5,4,3,3,6,7};
int [] res = fun(arr,3);
for(int a:res){
System.out.print(a+" ");
}
}
//计算每个窗口最大值并保存到数组返回
public static int [] fun(int [] arr,int w){
int [] res = new int[arr.length-w+1];
for(int i=0;i<arr.length-w+1;i++){
res[i] = getmax(arr,i,w);
}
return res;
}
//寻找一个窗口最大值
public static int getmax(int [] arr,int start,int w){
int max = arr[start];
for(int i=start+1;i<start+w;i++){
if(arr[i]>max){
max = arr[i];
}
}
return max;
}
}
解题方法2
- 可以使用双端队列进行优化,队列中存放元素的下标。
- 队头弹出过期的下标,队尾压入值更小或等的下标,弹出值更大的下标。
- 这样可以保证队头元素指向的永远是当前窗口最大值。
- 我们根据arr数组当前遍历位置i往前推w个元素便可以判断队头是否还在窗口范围内(是否过期)。
- 代码如下:
public class Test {
public static void main(String[] args) {
int [] arr = {4,3,5,4,3,3,6,7};
int [] res = fun(arr,2);
for(int a:res){
System.out.print(a+" ");
}
}
//计算每个窗口最大值并保存到数组返回
public static int [] fun(int [] arr,int w){
int [] res = new int[arr.length-w+1];
LinkedList<Integer> queue = new LinkedList<>();
int index = 0; //index指向res数组的下标
for(int i=0;i<arr.length;i++){
//队列为空 或者 i指向元素<当前队尾 将i压入队尾
if(queue.size()==0 || arr[i]<queue.peekLast()){
queue.addLast(i);
}
//否则 弹出当前队尾继续比较 直到当前i指向值<队尾或者队列为空 然后再将i压入队尾
else{
while(queue.size()>0 && arr[i]>=arr[queue.peekLast()]){
queue.pollLast();
}
queue.addLast(i);
}
//如果当前队头过期了 就把队头出队
if(queue.peekFirst()<(i-w+1)){
queue.pollFirst();
}
//如果当前可以构成一个窗口了 就将队头保存到数组res
if((i-w+1)>=0){
res[index] = arr[queue.peekFirst()];
index++;
}
}
return res;
}
}