【Lintcode】362. Sliding Window Maximum

題目地址:

https://www.lintcode.com/problem/sliding-window-maximum/description

給定一個數組,再給定一個正整數kk,求數組所有長度爲kk的滑動窗口最大數字。

思路是單調隊列。維護一個單調下降的雙端隊列(從隊頭到隊尾嚴格下降),每遇到一個數xx的時候,先將隊尾小於等於xx的數poll出來,再將隊頭出了滑動窗口的數也poll出來,再把xx加入隊列。這樣隊頭維護的就是滑動窗口的最大值。注意,由於要將隊頭出了滑動窗口的數也poll出來,如果隊列存的是數組中的數字本身的話,會很不方便,所以隊列存的實際上是數的下標。代碼如下:

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;

public class Solution {
    /**
     * @param nums: A list of integers.
     * @param k: An integer
     * @return: The maximum number inside the window at each moving.
     */
    public List<Integer> maxSlidingWindow(int[] nums, int k) {
        // write your code here
        List<Integer> res = new ArrayList<>();
        Deque<Integer> deque = new ArrayDeque<>();

		// 我們計算以nums[i]爲最後元素的滑動窗口中的最大數
        for (int i = 0; i < nums.length; i++) {
        	// 將隊尾小於等於nums[i]的下標出隊
            while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
                deque.pollLast();
            }
            // 如果隊首下標出了窗口,也出隊
            if (!deque.isEmpty() && deque.peekFirst() <= i - k) {
                deque.pollFirst();
            }
            // 最後再將當前數字下標入隊
            deque.offerLast(i);
            
            if (i >= k - 1) {
            	// 隊頭存的是滑動窗口最大數的下標,加入最終結果res
                res.add(nums[deque.peekFirst()]);
            }
        }
        
        return res;
    }
}

時間複雜度O(n)O(n),空間O(k)O(k)

算法正確性證明:
首先證明隊列中數字保持單調。對ii進行數學歸納法。當i=0i=0時循環結束時隊列裏只有一個數,顯然成立;假設對i=ki=k時成立,當i=k+1i=k+1時,由於隊列已經單調,所以小於A[i]A[i]的數都集中在隊尾,while循環中隊尾大於A[i]A[i]的數(下標)都會出隊,所以隊列維持單調不變。

接下來證明,對於每個ii,隊列裏從隊頭到隊尾存放的是從A[ik+1,...,i]A[i-k+1,...,i]的數中,最大數在隊頭,最大數右邊的數中的最大數,在隊列第二位,此數右邊的數中的最大數,在隊列第三位,以此類推。也可以用數學歸納法來證明。由於每當新來一個數,都會把隊尾不超過它的數PK下來,並且把出了窗口的數也出隊,所以這一條結論顯然成立。那麼隊頭就維護了窗口最大值。所以算法正確。

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