滑动窗口(全面清晰/Java)

数组模拟单调队列

分析

以k=3举例:

(1)利用单调队列的性质:
<1>最小值:确保队列单调递增,处理后,队头即是最小值。
<2>最大值:确保队列单调递减,处理后,队头即是最大值。
那么怎么确保单调性?

关键代码:
以最小值为例:
while(hh<=tt&&a[q[tt]]>=a[i])tt--;

(2)这里的a[q[tt]]>=a[i]即是确保单调性的关键!
(求最大值这里修改成a[q[tt]]<=a[i],确保单调递减即可,其余均一致。)

解读:每次添加元素到队尾后,将该队尾元素和队头元素进行比较,如果这个元素大于(等于)队头元素,就说明队尾元素并不是需要的最小值,队头元素比它更适合,更有潜力成为最小值,所以将该队尾元素抹掉,则t- -。再继续添加,继续判断。

换句话来说,不满足单调递增的性质,需要将不满足的元素给剔掉,确保单调性。

确保单调性后,为什么输出队头即可?
确保单调性后,我们得到的队尾元素即是最小/大值,此时,如果只有一个元素,即队头,输出队头即可。
如果还有剩余元素,需要将队列中剩余的其他元素剔掉,让队列中只有一个元素,此时,队头指向队尾,输出队头即可。
综上,输出队头即可。

那又怎么剔掉单调处理后,队列中还有多余的元素(出队多余元素)?
关键代码:
以最小值为例:
if(hh<=tt&&i-k+1>q[hh])hh++;

(3)这里的i-k+1即是确保出队的关键!
推断如下:

这里的出队有两层意义:

<1>用于更新原队列的hh,一个个出掉,范围更新下去。
<2>用于队头元素的出队,如果tt- -后,队列中只有1个元素,即队头,输出即可。
如果队列中还剩1、2个元素,则需要出队,不断出掉队尾元素前的元素,即hh++;

(4)最后,由单调性的确保和队头元素的确保,只需要输出队头元素即可。

队列过程变化图

图1

滑动窗口(全面清晰/Java)_第1张图片

图2

滑动窗口(全面清晰/Java)_第2张图片

代码

import java.util.*;
import java.io.*;
public class Main{
	static int N=100010;
	static int n,k;
	static int hh,tt;
	static int q[]=new int [N];
	static int a[]=new int [N];
	
    public static void main(String []args) throws IOException{
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
        String []st = in.readLine().split(" ");
        n=Integer.parseInt(st[0]);
        k=Integer.parseInt(st[1]);
        String []str = in.readLine().split(" ");
     for(int i=0;i<n;i++){
         a[i]=Integer.parseInt(str[i]);
     }
     
     hh = 0;
     tt=-1;
     //求最小值
     for(int i=0;i<n;i++){
        //出队
        if(hh<=tt&&i-k+1>q[hh])hh++;//出队
        //t--更新到最后,如果还剩1-2个元素,则把他们都剔掉。
        //此时,队头指向队尾,再输出队头即可。
        
        while(hh<=tt&&a[q[tt]]>=a[i])tt--;//保证队列单调递增,那么队尾即是最小值。
    //具体为添加进队尾的元素如果比队头的元素要大,就tt--,即把该元素给剔掉。
        
        q[++tt]=i;//添加元素下标到队列尾
        
        if(i>=k-1)//在k的范围队列中,输出元素
        out.print(a[q[hh]]+" ");
     }
     out.println();
     
    //求最大值
    hh=0;
    tt=-1;
     for(int i=0;i<n;i++){
        //出队
        if(hh<=tt&&i-k+1>q[hh])hh++;
   //t--更新到最后,如果还剩1-2个元素,则把他们都剔掉。
  //此时,队头指向队尾,再输出队头即可。
        
        while(hh<=tt&&a[q[tt]]<=a[i])tt--;//保证队列单调递减,那么队尾即是最大值。
  //具体为添加进队尾的元素如果比队头的元素要小,就tt--,即把该元素给剔掉。
         
        q[++tt]=i;//添加元素下标到队列尾
        
        if(i>=k-1)//在k的范围队列中,输出元素
        out.print(a[q[hh]]+" ");
    }
    out.flush();
}
}

你可能感兴趣的:(蓝桥杯上岸,java,滑动窗口,蓝桥杯,算法,数据结构,eclipse,leetcode)