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;
	
  }
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章