题目:
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]
.
Note:
You may assume k is always valid, ie: 1 ≤ k ≤ input array's size for non-empty array.
Follow up:
Could you solve it in linear time?
Hint:
- How about using a data structure such as deque (double-ended queue)?
- The queue size need not be the same as the window’s size.
- Remove redundant elements and the queue should store only elements that need to be considered.
链接: http://leetcode.com/problems/sliding-window-maximum/
题解:
长题目思密达。题目大意是给定一个数组和一个长为k的window,求这个window在从头到尾滑动时,每个window里的最大值组成的集合。考虑了很久,最后求助了Discuss。原理使用一个Deque,或者用doubly linkedlist也可以。我们队这个数组维护一个递减的双端队列,队列的内容为坐标index。每次移动时,移除小于当前队首坐标的元素,同时比较当前元素与队尾元素的大小,假如队尾元素较小,移除队尾元素,继续比较当前元素和队尾。比较完毕后把当前元素加入到队列中, 最后判断是否窗口已满,要输出到结果集中。
Time Complexity - O(n), Space Complexity - O(n)。
public class Solution { public int[] maxSlidingWindow(int[] nums, int k) { if(nums == null || nums.length == 0) return new int[]{}; int len = nums.length; int[] res = new int[len - k + 1]; Deque<Integer> dq = new LinkedList<>(); for(int i = 0; i < len; i++) { while(!dq.isEmpty() && dq.peekFirst() < i - (k - 1)) // maintain a window of length k dq.pollFirst(); while(!dq.isEmpty() && nums[dq.peekLast()] < nums[i]) // compare last elements with nums[i] dq.pollLast(); dq.offerLast(i); if(i >= k - 1) res[i - (k - 1)] = nums[dq.peekFirst()]; // since we have a descendent deque, first is always the largest in window } return res; } }
题外话:
最近感恩节,祝福大家感恩节快乐。这几天总在网上看deal,没有好好刷题,难得的4天休假也没有完全利用起来。Cousera的斯坦福算法1总算上完了,跟得很累,每周都要花不少时间。但是有一些思想和题目比较经典,比如closest pair,find inversions,matrix multiply,还有find median in data stream等等。有时间要好好复习一下。下面记录几个link有空时学习。
https://leetcode.com/discuss/64811/easy-to-understand-double-heap-solution-in-java
http://andrew-algorithm.blogspot.com/
http://www.stefan-pochmann.info/spam/
下周上一周班,周四有公司年会,之后那周是培训5天,我打算再请两周假,这样整个十二月份基本都能有时间好好做题和学习。 抽空要把加拿大签证办了,然后过去签美国签证,这样明年二月就可以顺利回国和顺便去趟日本玩了。
二刷:
这回也是使用一刷的方法,但是顺了很多。 也有更快速的方法,两次遍历输入入组,留给下一次再刷时来解决。
- 首先我们读完题目,确定是用sliding window的方法来做
- 我们需要先去除一些invalid case,并且判断出结果数组res的长度是 len - k + 1,这里len是输入数组的长度
- 然后我们建立一个doubly linked list叫做window,用来保存滑动窗口中出现的数组最大值的index,注意这里存的是index而不是value
- 现在我们可以开始遍历数组
- 首先,我们要查看window头部index,假如超过了 i - k,我们要从前部poll出这个index
- 接下来我们对比window尾部index位置上的值 和 当前nums[i],假如nums[i]更大,则我们poll出window的尾部index,再继续进行比较,直到window为空或者nums[i] < nums[window.peelLast()], 保证window中是一个递减排列
- 我们将i加入到window中
- 查看是否 i - (k - 1) >= 0,假如满足这个条件,则表示我们已经判断了至少k个元素,可以生成结果。
- 最后返回结果。 要注意写的过程中各种边界,真正面试过程中跑1 ~ 2个例子就会比较清晰。
Java:
Time Complexity - O(n), Space Complexity - O(n)。
public class Solution { public int[] maxSlidingWindow(int[] nums, int k) { if (nums == null || nums.length < k || k <= 0) return nums; int len = nums.length; int[] res = new int[len - k + 1]; LinkedList<Integer> window = new LinkedList<>(); for (int i = 0; i < len; i++) { if (!window.isEmpty() && window.peekFirst() <= i - k) window.pollFirst(); while (!window.isEmpty() && nums[window.peekLast()] < nums[i]) window.pollLast(); window.offerLast(i); if (i - (k - 1) >= 0) res[i - (k - 1)] = nums[window.peek()]; } return res; } }
Reference:
https://leetcode.com/discuss/47139/this-is-a-typical-monotonic-queue-problem
https://leetcode.com/discuss/99541/not-the-best-but-the-fastest-beat-97%25
https://leetcode.com/discuss/94516/simple-java-solution-that-beats-100%25-submissions
https://leetcode.com/discuss/83402/simple-java-solution-beats-98-87%25
https://leetcode.com/discuss/46578/java-o-n-solution-using-deque-with-explanation
https://leetcode.com/discuss/62695/o-n-solution-in-java-with-two-simple-pass-in-the-array