题目原文:
Given an array nums, 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 right by one position.
For example,
Given nums = [1,3,-1,-3,5,3,6,7], and k = 3.
Window position Max
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
Therefore, return the max sliding window as [3,3,5,5,6,7].
题目大意:
给出一个数组和一个k长度的滑动窗口,把滑动窗口从数组开头滑到结尾,求每个滑动窗口内子数组的最大值,组成一个新数组并返回。
题目分析:
朴素解法是暴力枚举每个滑动窗口的最大值,时间复杂度是O(nk),虽然也能ac,但一个hard难度的题不可能只要求到这步。
根据hint,考虑用双端队列,维持队列长度小于等于k,且队头永远为滑动窗口最大值,队列后面跟的是可能成为最大值的候选解。每次加入一个元素,先看队头元素的下标是否已经在滑动窗口之外,如果在则出队,接下来判断这个元素是否大于队尾的值,如果大于则从队尾一直弹出(因为有了这个更大的数,前面的数在往右滑的过程中不可能是解),每次记录下队头的值,即为当前滑动窗口的最大值。
那么还有一个问题,如何知道队头是否滑到了窗口外面呢?答案是,我们的deque只要存的是下标就可以了。因为队列的操作全都是常数时间的,这样的时间复杂度就降到了O(n).
源码:(language:java)
public class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums.length==0)
return new int[0];
int[] window = new int[nums.length-k+1];
int j=0;
Deque<Integer> deque = new LinkedList<>();
for(int i = 0;i<nums.length;i++) {
while (!deque.isEmpty() && nums[deque.getLast()] < nums[i]) {
deque.removeLast();
}
deque.addLast(i);
if(deque.getFirst() == i-k)
deque.removeFirst();
if(i>=k-1)
window[j++] = nums[deque.getFirst()];
}
return window;
}
}
成绩:
29ms,beats 62.07%,众数31ms,9.54%
Cmershen的碎碎念:
这道题在Leetcode里面算是难题了,且对面试来说算是比较困难的一题,Leetcode的国内山寨版Lintcode的难度评分中将其评为Very Hard难度(lintcode里面very hard难度只有两题,另一题是涉及到线段树的skyline problem),足见此题对普通面试者的难度。
此外discuss中还有一个仅4ms的解法,有机会研读一下。