Sliding Window(POJ2823:滑动窗口求最值) --------Java从0开始学习系列之路(7)

前言:

在教室被蚊子叮,福州天气太热导致身体黏糊糊,但是又不想回宿舍。。

Sliding Window(POJ2823,跟左神书上的滑动窗口一致)

题目:

Time Limit: 12000MS   Memory Limit: 65536K
Total Submissions: 72493   Accepted: 20593
Case Time Limit: 5000MS

Description

An array of size n ≤ 106 is given to you. There is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves rightwards by one position. Following is an example: 
The array is [1 3 -1 -3 5 3 6 7], and k is 3.

Window position Minimum value Maximum value
[1  3  -1] -3  5  3  6  7  -1 3
 1 [3  -1  -3] 5  3  6  7  -3 3
 1  3 [-1  -3  5] 3  6  7  -3 5
 1  3  -1 [-3  5  3] 6  7  -3 5
 1  3  -1  -3 [5  3  6] 7  3 6
 1  3  -1  -3  5 [3  6  7] 3 7

Your task is to determine the maximum and minimum values in the sliding window at each position. 

Input

The input consists of two lines. The first line contains two integers n and k which are the lengths of the array and the sliding window. There are n integers in the second line. 

Output

There are two lines in the output. The first line gives the minimum values in the window at each position, from left to right, respectively. The second line gives the maximum values. 

Sample Input

8 3
1 3 -1 -3 5 3 6 7

Sample Output

-1 -3 -3 -3 3 3
3 3 5 5 6 7

题意:

给你n,k。n代表元素的个数,k代表窗口的长度。

如上面 表格所示,将窗口从左向右滑动,在这个过程中,求窗口在每个位置时窗口内的最小值和最大值。

分析:

这题n为1e6. k<=n。如果用暴力解法,也就是枚举出窗口的n -k +1个位置,然后再遍历求两个最值,这样的话实在是太暴力,复杂度将会达到O(n*k),铁定超时。So,一个O(n)复杂度的解法是非常重要的,这个做法也是非常经典的。

O(N)复杂度解法:

什么是双端队列?双端队列(deque,全名double-ended queue)是一种具有队列和栈性质的抽象数据类型。双端队列中的元素可以从两端弹出,插入和删除操作限定在队列的两边进行。

用一个dequeue。每当窗口向右滑动一个格子的时候,其实就是原窗口最左边的数不再被涵盖,新涵盖了刚移动到的格子。

这个时候我们只要把这个数(称为cur)和队列的尾端的元素比较一下关系(以求最大值为例,最小值跟最大值做法几乎完全一样):

如果 尾端的数 <  cur, 那么就弹出尾端的数,不断重复,直到尾端的数>=cur 才停止,然后再把cur加入dequeue的尾端。

如果尾端的数>=cur,那么就直接把Cur加入尾端。

接着再判断一下,看看移动后head对应的位置是不是在新窗口的范围内,也就是要保证,head的最值必须处于窗口当前的范围之内,要是下标过期了,就要head++,从头部弹出。

(emmm,其实很好理解的,只要一开始我就按照上面的规则来做,那么每当向右滑动一个位置的时候,队列中的head元素一定是原窗口最值对应的下标,且队列中的元素一定是原窗口内的单调队列,很简单,按照上面这样规则,每个数一定都会进一次队列,但是每个数从尾部弹出的原因是:cur大于自己,导致自己被弹出,也就是被第一个大于自己的数给淘汰了。)

为了帮助大家理解下标过期的情况,我举例一下吧(依然是以求最大值为例):

【5   3    2 】  4   此时head对应的位置是下标0,也就是5,tail对应的下标是2,也就是2.

   5  【 3    2    4】向右滑动到4,此时由于4>尾端的2,所以依次弹出3,2。然后发现head 对应的下标是0,也就是5,此时5不在新窗口的范围内了,所以应该head++ ,来把对应的下标0弹出。(原队列中的数,一定是原窗口内的单调队列,所以再添加一个数之后,虽然也是构成了一个单调队列,但是单调队列的最值有可能是原窗口的左端点,所以应该要判断一下,这样一想,感觉就是很正确!!!嘻嘻嘻

附AC代码(c++,数组模拟dequeu,用c++的优化才能卡时间AC)

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define N 1000006
int n,k,headMax,tailMax,headMin,tailMin;
int s[N],ansMin[N],ansMax[N],dequeueMax[N],dequeueMin[N];

void slide(){

     headMax = tailMax = 0;
     headMin = tailMin = 0;

    for(int i = 0 ;i < n ; i++){


            while(headMax != tailMax && s[i] > s[dequeueMax[tailMax - 1]] ){

                tailMax--;
            }

            dequeueMax[ tailMax++ ] = i;




             while(headMin != tailMin && s[i] < s[dequeueMin[tailMin - 1]] ){

                tailMin--;
            }
            dequeueMin[ tailMin++ ] = i;




      if( i >= k -1 &&  dequeueMax[ headMax ] < i - k + 1 )
            headMax++;
        ansMax[i] = s[ dequeueMax[headMax] ];

       if( i >= k -1 &&  dequeueMin[ headMin ] < i - k + 1 )
            headMin++;
        ansMin[i] = s[ dequeueMin[headMin] ];


    }
}
int main()
{
    while( ~scanf("%d %d",&n,&k)){

    for(int i = 0 ;i < n ;i++)
        scanf("%d",&s[i]);

 
    slide();

    for(int i = k -1  ;i < n; i++)
        printf("%d ",ansMin[i]);

    printf("\n");

    for(int i = k -1 ;i < n  ;i++)
         printf("%d ",ansMax[i]);
    printf("\n");



    }

    return 0;
}

附AC代码(Java,依然是数组模拟dequeue)

package code_180;

import java.util.*;

public class SlideWindow {
	
	private int s[] ;
	private int dequeueMax [];
	private int dequeueMin[];
	private int ansMax[];
	private int ansMin[];
	private int sLen ;
	private int wLen;
	private int headMax ;
	private int headMin;
	private int tailMax ;
	private int tailMin;
	
	SlideWindow(int n ,int k) {
		
		sLen = n;
		wLen = k;
		s = new int[n];
		dequeueMax = new int [n];
		dequeueMin = new int[n];
		ansMax = new int [n];
		ansMin = new int[n];
		headMax = tailMax = 0;
		headMin = tailMin = 0;
	}

	public void slide() {
		
		 headMax = tailMax = 0;
	     headMin = tailMin = 0;

	    for(int i = 0 ;i < sLen ; i++){


	            while(headMax != tailMax && s[i] > s[dequeueMax[tailMax - 1]] ){

	                tailMax--;
	            }

	            dequeueMax[ tailMax++ ] = i;




	             while(headMin != tailMin && s[i] < s[dequeueMin[tailMin - 1]] ){

	                tailMin--;
	            }
	            dequeueMin[ tailMin++ ] = i;




	      if( i >= wLen -1 &&  dequeueMax[ headMax ] < i - wLen + 1 )
	            headMax++;
	        ansMax[i] = s[ dequeueMax[headMax] ];

	       if( i >= wLen -1 &&  dequeueMin[ headMin ] < i - wLen + 1 )
	            headMin++;
	        ansMin[i] = s[ dequeueMin[headMin] ];


	    }
	}
	

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Scanner cin = new Scanner(System.in);

		
		int n= cin.nextInt();
		int k = cin.nextInt();
		
		
		SlideWindow myWindow = new SlideWindow(n,k);
		
		for(int i = 0 ; i< myWindow.sLen; i++)
			myWindow.s[i] = cin.nextInt();
		
		myWindow.slide();
		
		for(int i =myWindow.wLen - 1; i< myWindow.sLen ;i++)
			System.out.print( myWindow.ansMin[i]+" ");
		System.out.println();
		
		for(int i =myWindow.wLen - 1; i< myWindow.sLen ;i++)
			System.out.print( myWindow.ansMax[i]+" ");
		
		System.out.println();
	
        /*
		Slides myWindow = new Slides(n,k);
		for(int i = 0 ; i< myWindow.sLen; i++)
			myWindow.s[i] = cin.nextInt();
		
		myWindow.slide();
		
		for(int i =myWindow.wLen - 1; i< myWindow.sLen ;i++)
			System.out.print( myWindow.ansMin[i]+" ");
		System.out.println();
		
		for(int i =myWindow.wLen - 1; i< myWindow.sLen ;i++)
			System.out.print( myWindow.ansMax[i]+" ");
		
		System.out.println();
        */
		
		
	
	}
}
class Slides{                //LinkedList做法,一样的
	
	private LinkedList<Integer> dequeueMax ;
	private LinkedList<Integer>dequeueMin;
	int s[];
    int ansMax[];
    int ansMin[];
	int wLen ;
	int sLen;
	
	Slides(int n ,int k){
		
		wLen = k;
		sLen = n;
		s = new int [n];
		dequeueMax = new LinkedList<Integer>();
		dequeueMin = new LinkedList<Integer>();
		ansMax = new int [n];
		ansMin = new int [n];
	}
	
	public int slide(){
		
		if(sLen < wLen || sLen == 0 || wLen == 0)
			return 0;
        
		else {
			
			for(int i = 0 ; i < sLen ;i++) {
			
				while(!dequeueMax.isEmpty() && s[i] > s[dequeueMax.peekLast()]) {
				
					dequeueMax.pollLast();
			}
				
			dequeueMax.addLast(i);
			
			if(dequeueMax.peekFirst() < i - wLen + 1)
				dequeueMax.pollFirst();
			
			ansMax[i] = s[dequeueMax.peekFirst()];
			
			
			while(!dequeueMin.isEmpty() && s[i] < s[dequeueMin.peekLast()]) {
				
				dequeueMin.pollLast();
			}
			dequeueMin.addLast(i);
			
			if(dequeueMin.peekFirst() < i - wLen + 1)
				dequeueMin.pollFirst();
			
			ansMin[i] = s[dequeueMin.peekFirst()];
			
			
			
		}
	 }
		
		return 1;
	
  }
}

猜你喜欢

转载自blog.csdn.net/CCSGTC/article/details/82747406