【棧與隊列】移動窗口生成最大值的數組

功能需求

        有一個整數型數組arr和一個大小爲w的窗口從數組的最左端滑到最右端,窗口每次向右滑動一個位置。就像是一個滑動的指針,從頭指向尾,然後輸出窗口中數據的最大值。

        例如,一組數據爲arr[]  = [4,3,5,4,3,3,6,7],窗口大小爲w = 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                           窗口中最大值爲 5

          4    3    5    4  [  3    3    6  ]  7                           窗口中最大值爲 5

          4    3    5    4    3  [  3    6    7  ]                         窗口中最大值爲 5

        如果數組的長度爲n,窗口大小爲w,這一共產生n-w-7個窗口的最大值。

    要求:請實現一個函數。

        輸入:整型數組arr,窗口大小爲w

        輸出:一個長度爲n-w-1的輸入res,res[ i ]表達每一種窗口狀態下的最大值。{5,5,5,4,6,7}.

詳細解析

        如果數據長度爲N,窗口大小爲w,如果做出時間複雜度O(N*w)的解法是不能讓面試官滿意的,本題要求面試者想出的時間複雜度O(N)的實現。所以本題的關鍵在於利用雙端隊列來實現窗口最大值的更新。首先生成的是雙端隊列qmax,qmax中存放着數組arr中的下標。

    假設:遍歷到arr[i],qmax的放入規則爲:

    1、如果qmax爲空,直接把下標i放入qmax,放入過程結束。

    2、如果qmax不爲空,取出當前qmax隊尾放入隊尾存放的下標,假設爲j。

        1)如果arr[j] > arr[i],直接把下標i放入qmax的隊尾,放入過程結束。

        2)如果arr[j] < arr[i],把j從qmax中彈出,繼續qmax的放入規則。

    假設遍歷到arr[i],qmax的彈出規則爲:

        如果qmax隊頭的下標等於i-w,說明當前qmax隊頭下標已經過期,彈出當前對頭下標即可。根據如上的放入的彈出規則,qmax便成了一個維護窗口爲w的子數組的最大值更新的結構。舉例如下;

        1、開始時qmax爲空,qmax={}

        2、遍歷到arr[0] == 4,將下標0放入qmax,qmax= { 0 }。

        3、遍歷到arr[1] == 3,當前qmax的隊尾下標爲0,又有arr[0] > arr[1],所以將下標1放入qmax尾部,qmax={ 0 , 1}。

        4、遍歷到arr[2] == 5,當前qmax的隊尾下標爲1,又有arr[1] <= arr[2],所以將下標1從qmax的尾部彈出,qmax變成{0}。當前qmax的隊尾下標爲0,又有arr[0] <= arr[2],所以將下標0從qmax尾部彈出,qmax變成{}。將下標2放入qmax,qmax={2}。此時已經遍歷到下標2的位置,窗口arr[0,2]出現,當前qmax對頭的下標爲2,所以窗口arr[0,2]的最大值爲arr[2](即爲5)。

       5、遍歷到arr[3]==4,當前qmax的隊尾下標爲2,又有arr[2]>arr[3],所以將下標了放入qmax尾部,qmax={2,3}。 窗口arr[1..3]出現,  當前qmax隊頭的下標爲2,這個下標還沒有過期,所以窗口ar[1..3]的最大值爲arr[2] (即5)。

        6、遍歷到arr[4]== =3,當前qmax的隊尾下標爲3,  又有arr[3]>arr[4], 所以將下標4放入qmax尾部,qmax={2,3,4}。 窗口arr[2..4]出現,當前qmax隊頭的下標爲2,這個下標還沒有過期,所以窗口arr[2..4]的最大值爲arr[2] (即5)。

        7、遍歷到arr[5]==3,當前qmax的隊尾下標爲4,  又有arr[4]<= =arr[5],所以將下標4從qmax的尾部彈出,qmax變爲{2,3}。當前qmax的隊尾下標爲了,又有arr[3]>arr[5],所以將下標5放入qmax尾部,qmax= {2,3,5}。窗口arr[3..5]出現,當前qmax隊頭的下標爲2,這個下標已經過期,所以從qmax的頭部彈出,  qmax變爲{3,5}。當前qmax隊頭的下標爲3,這個下標沒有過期,所以窗口arr[3..5]的最大值爲arr[3] (即4)。

        8、遍歷到arr[6]==6, 當前qmax的隊尾下標爲5,又有arr[5]<=arr[6], 所以將下標5從qmax的尾部彈出,qmax變爲{3}。當前qmax的隊尾下標爲3,  又有arr[3]<=arr[6],所以將下標3從qmax的尾部彈出,qmax變爲{}。將下標6放入qmax,qmax={6}。窗口ar([..]出現,當前qmax隊頭的下標爲6,這個下標沒有過期,所以窗口arr[4..6]的 最大值爲arr[6](即6)。

        9、 遍歷到arr[7]==7, 當前qmax的隊尾下標爲6,又有arr[6]<=arr[7], 所以將下標6 從qmax的尾部彈出,qmax 變爲{}。將下標7放入qmax,qmax={7}。 窗口arr[5..7]出現,當前  qmax隊頭的下標爲7,這個下標沒有過期,所以窗口arr[5..7]的最 大值爲arr[7](即7)。

        10、依次出現的窗口最大值爲[5,5,5,4,6,7],在遍歷過程中收集起來,最後返回即可。

具體過程參看如下代碼中的getMax Window方法。

代碼實現

// 移動窗口獲取最大值方法
public int[] getMaxWindow(int[] arr,int w) {
	// 判斷數組信息或者截取長度時候合格
	if (arr == null || w < 1 || arr.length < w) {
		// 返回方法
		return;
	}
	// 實例化一個鏈表結構
	LinkedList<Integer> qmax = new LinkedList<Integer>();
	// 實例化一個int類型數組並且定義長度爲執行的次數
	int[] res = new int[arr.length - w + 1];
	// 定義一個下標常量
	int index = 0;
	// 循環遍歷數組
	for (int i = 0 ; i < arr.length ; i++) {
		// 判斷數組中時候還有數據,或者已經遍歷到隊尾
		while (!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[i]) {
			// 移除對頭元素
			qmax.pollLast();
		}
		// 隊尾添加最後一個元素
		qmax.addLast(i);
		// 判斷對頭元素時候爲最後一個元素
		if (qmax.peekFirst() == i - w) {
			// 出隊第一個元素
			qmax.pollFirst();
		}
		// 如果移動到最後元素前
		if (i >= w - 1) {
			// 把得到的數組存入res集合中
			res[index++] = arr[qmax.peekFirst()];
		}
	}
	// 返回信息的集合
	return res;
}

總結分析

雙端隊列,將arr中的元素加入res該隊列中,若該隊列的隊尾元素小於等於要加入的元素,則不斷的彈出,直到隊尾元素大於該元素或者隊列爲空。此時將該元素的序號加入隊列中。同時當 i-w == 隊頭的序號,則將隊頭元素彈出。上述過程中,  每個下標值最多進qmax-一次,  出qmax一-次。所以遍歷的過程中進出雙端隊列的操作是時間複雜度爲O(N),整體的時間複雜度也爲O(N)。寫完手工。。睡覺

 

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