題目地址:
https://www.lintcode.com/problem/sliding-window-maximum/description
給定一個數組,再給定一個正整數,求數組所有長度爲的滑動窗口最大數字。
思路是單調隊列。維護一個單調下降的雙端隊列(從隊頭到隊尾嚴格下降),每遇到一個數的時候,先將隊尾小於等於的數poll出來,再將隊頭出了滑動窗口的數也poll出來,再把加入隊列。這樣隊頭維護的就是滑動窗口的最大值。注意,由於要將隊頭出了滑動窗口的數也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;
}
}
時間複雜度,空間。
算法正確性證明:
首先證明隊列中數字保持單調。對進行數學歸納法。當時循環結束時隊列裏只有一個數,顯然成立;假設對時成立,當時,由於隊列已經單調,所以小於的數都集中在隊尾,while循環中隊尾大於的數(下標)都會出隊,所以隊列維持單調不變。
接下來證明,對於每個,隊列裏從隊頭到隊尾存放的是從的數中,最大數在隊頭,最大數右邊的數中的最大數,在隊列第二位,此數右邊的數中的最大數,在隊列第三位,以此類推。也可以用數學歸納法來證明。由於每當新來一個數,都會把隊尾不超過它的數PK下來,並且把出了窗口的數也出隊,所以這一條結論顯然成立。那麼隊頭就維護了窗口最大值。所以算法正確。