/**
* 问题: 生成窗口最大值数组
* 有一个整形数组array和一个大小为w的窗口从数组最左边滑到最右边,窗口每次向右边滑动一个位置。
* 例如,数组为[4,3,5,4,3,3,6,7],窗口大小为3时:
* [4 3 5] 4 3 3 6 7 窗口的最大值是5
* 4 [3 5 4] 3 3 6 7 窗口的最大值是5
* 4 3 [5 4 3] 3 6 7 窗口的最大值是5
* 4 3 5 [4 3 3] 6 7 窗口的最大值是4
* 4 3 5 4 [3 3 6] 7 窗口的最大值是6
* 4 3 5 4 3 [3 6 7] 窗口的最大值是7
*
* 如果数组的长度为n,窗口大小为w,则一定会生成n-w+1个窗口的最大值
* 要求实现一个函数
* 1.输入:整形数组array,窗口大小w
* 2.输出:一个长度为n-w+1的数组res,res[i]表示每一种窗口状态下的最大值。
* 3.算法的时间复杂度为O(n)
* 分析:
* 本题的关键在于利用双端队列来实现窗口最大值的更新。首先生成双端队列qmax,qmax中存放数组
* array中的下标。
* 假设遍历到array[i],qmax的放入规则为:
* 1.如果qmax为空,直接把下标i放进qmax,放入过程结束。
* 2.如果qmax不为空,取出当前qmax队尾存放的下标,假设为j。
* 1.如果array[j]>array[i],直接把下标i放到qmax的队尾,放入过程结束。
* 2.如果array[j]<=array[i],把j从qmax中弹出,重复qmax的放入规则。
* 实例:
* 下标: 0 1 2 3 4 5 6 7
* 大小: 4 3 5 4 3 3 6 7
*
*1.开始时qmax为空,即qmax={}。
*2.遍历到array[0]=4时:
*由于qmax为空,则将下标0放入到qmax内,此时qmax={0}。
*3.遍历到array[1]=3时:
*由于qmax的尾端下标为0,由array[0]>array[1],则将下标1放入到qmax中,此时qmax={0,1}。
*4.遍历到array[2]=5时:
*此时窗口array[0,1,2]出现。由于qmax的末尾下标为1,根据array[1]<array[2],故将下标1从qmax末尾弹出,此时qmax={0}。
*又有array[0]<array[2],故将下标0从qmax末尾弹出,此时qmax={}。
*由于qmax为空,则将下标2放入到qmax中,此时qmax={2}。
*由于窗口为[0,1,2],此时下标2还没有过期,故窗口[0,1,2]的最大值是下标2所对应的array[2]。
*5.遍历到array[3]=4时:
*此时窗口[1,2,3]出现,由于qmax的末尾下标为2,根据array[2]>array[3],故将下标3放入到qmax中,此时qmax={2,3}。
*由于窗口为[1,2,3],此时下标2还没有过期,故窗口[1,2,3]的最大值是下标2所对应的array[2]。
*6.遍历到array[4]=3时:
*此时窗口[2,3,4]出现,由于qmax的末尾下标为3,根据array[3]>array[4],故将下标4放入到qmax中,此时qmax={2,3,4}。
*由于窗口为[2,3,4],此时下标2还没有过期,故窗口[2,3,4]的最大值是下标2所对应的array[2]。
*7.遍历到array[5]=3时:
*此时窗口[3,4,5]出现,由于qmax的末尾下标为4,根据array[4]=array[5],故将下标4弹出,此时qmax={2,3}。
*由于此时qmax的末尾下标为3,且array[3]>array[5],故将下标5放入到qmax中,此时下标为qmax={2,3,5}。
*由于窗口为[3,4,5],此时下标2已经过期,所以将下标2从qmax的头部弹出,此时qmax={3,5}。故窗口[3,4,5]的最大值是下标3所对应的array[3]。
*8.遍历到array[6]=6时:
*此时窗口[4,5,6]出现,由于qmax的末尾下标为5,根据array[5]<array[6],故将下标5弹出,此时qmax={3}。
*由于qmax的末尾下标为3,根据array[3]<array[6],故将下标3弹出,此时qmax={}为空。
*由于qmax为空,则将下标6存入qmax中,此时qmax={6}。
*由于窗口为[4,5,6],此时下标6还没有过期,故窗口[4,5,6]的最大值是下标6所对应的array[6]。
*9.遍历到array[7]=7时:
*此时窗口[5,6,7]出现,由于qmax的末尾下标为6,根据array[6]<array[7],故将下标6弹出,此时qmax={},为空。
*由于qmax为空,则将下标7存入qmax中,此时qmax={7}。
*由于窗口为[5,6,7],此时下标7还没有过期,故窗口[5,6,7]的最大值是下标7所对应的array[7]。
*
*上述过程中,每个下标最多进入qmax一次,出qmax一次,所以遍历过程中进出双端队列的时间复杂度是O(n)。
*
* @author DELL
*
*/
*代码
import java.util.LinkedList; public class getMaxArray { public int[] getMaxWindow(int array[],int w) { if(array==null||w<1||array.length<w) { throw new RuntimeException("error!your data is not true"); } LinkedList<Integer> qmax = new LinkedList<>(); //存放的最大值的数组内元素个数最大为n-w+1 int res[] = new int[array.length-w+1]; int index = 0; for(int i=0;i<array.length;i++) { //qmax不为空,且末尾下标对应的数组值小于当前遍历的值,则弹出下标 //直到qmax为空,或者是末端下标值大则停止遍历将数据追加到双端链表内 while(!qmax.isEmpty() && array[qmax.peekLast()]<array[i]) { qmax.pollLast(); } qmax.addLast(i); //判断下标是否过期 if(qmax.peekFirst() ==(i-w)) { qmax.pollFirst(); } if(i>= (w-1)) { res[index++] = array[qmax.peekFirst()]; } } return res; }
import java.util.Random;
import java.util.Scanner;
public class testGetMaxArray {
public void showArray(int array[]) {
for(int i:array) {
System.out.print(i+" ");
}
}
public static void main(String[] args) {
getMaxArray test = new getMaxArray();
testGetMaxArray show = new testGetMaxArray();
Random rand = new Random();
Scanner sc = new Scanner(System.in);
int N;
int w;
System.out.println("请输入数组长度:");
N=sc.nextInt();
System.out.println("请输入窗口宽度:");
w=sc.nextInt();
int array[]= new int[N];
int res[] = new int[N-w+1];
for(int i=0;i<N;i++) {
array[i]=(rand.nextInt(10)+1);
}
System.out.println("结果如下");
show.showArray(array);
System.out.println();
res = test.getMaxWindow(array, w);
show.showArray(res);
}
}