[leedcode 239] Sliding Window Maximum

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?

public class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
       /* 如果采用蛮力法,这个问题似乎不难解决:可以扫描每一个滑动窗口的所有数字并找出其中的最大值。如果滑动窗口的大小为k,需要O(k)时间才能找出滑动窗口里的最大值。对于长度为n的输入数组,这个算法总的时间复杂度是O(nk)。 
  实际上一个滑动窗口可以看成是一个队列。当窗口滑动时,处于窗口的第一个数字被删除,同时在窗口的末尾添加一个新的数字。这符合队列的先进先出特性。如果能从队列中找出它的最大数,这个问题也就解决了。 
  我们实现了一个可以用O(1)时间得到最小值的栈。同样,也可以用O(1)时间得到栈的最大值。同时我们讨论了如何用两个栈实现一个队列。综合这两个问题的解决方法,我们发现如果把队列用两个栈实现,由于可以用O(1)时间得到栈中的最大值,那么也就可以用O(1)时间得到队列的最大值,因此总的时间复杂度也就降到了O(n)。 我们可以用这个方法来解决问题。不过这样就相当于在一轮面试的时间内要做两个面试题,时间未必够用。再来看看有没有其它的方法。 
  下面换一种思路。我们并不把滑动窗口的每个数值都存入队列中,而只把有可能成为滑动窗口最大值的数值存入到一个两端开口的队列。接着以输入数字{2,3,4,2,6,2,5,1}为例一步分析。 
  数组的第一个数字是2,把它存入队列中。第二个数字是3.由于它比前一个数字2大,因此2不可能成为滑动窗口中的最大值。2先从队列里删除,再把3存入到队列中。此时队列中只有一个数字3.针对第三个数字4的步骤类似,最终在队列中只剩下一个数字4.此时滑动窗口中已经有3个数字,而它的最大值4位于队列的头部。 
  接下来处理第四个数字2。2比队列中的数字4小。当4滑出窗口之后2还是有可能成为滑动窗口的最大值,因此把2存入队列的尾部。现在队列中有两个数字4和2,其中最大值4仍然位于队列的头部。 
  下一个数字是6.由于它比队列中已有的数字4和2都大,因此这时4和2已经不可能成为滑动窗口中的最大值。先把4和2从队列中删除,再把数字6存入队列。这个时候最大值6仍然位于队列的头部。 
  第六个数字是2.由于它比队列中已有的数字6小,所以2也存入队列的尾部。此时队列中有两个数字,其中最大值6位于队列的头部。 
  接下来的数字是5.在队列中已有的两个数字6和2里,2小于5,因此2不可能是一个滑动窗口的最大值,可以把它从队列的尾部删除。删除数字2之后,再把数字5存入队列。此时队列里剩下两个数字6和5,其中位于队列头部的是最大值6. 
  数组最后一个数字是1,把1存入队列的尾部。注意到位于队列头部的数字6是数组的第5个数字,此时的滑动窗口已经不包括这个数字了,因此应该把数字6从队列删除。那么怎么知道滑动窗口是否包括一个数字?应该在队列里存入数字在数组里的下标,而不是数值。当一个数字的下标与当前处理的数字的下标之差大于或者等于滑动窗口的大小时,这个数字已经从滑动窗口中滑出,可以从队列中删除了。*/
  //如果比尾节点小,要add,如果比尾节点大,要弹出尾节点,最后add!!
        LinkedList<Integer> queue=new LinkedList<Integer>();
        int len=nums.length;

        if(len<=0||k>len) return new int[0];
        int[] res=new int[len-k+1];
        for(int i=0;i<k-1;i++){
            while(!queue.isEmpty()&&nums[i]>nums[queue.getLast()]){
                queue.removeLast();
            }
            queue.add(i);
        }
        for(int i=k-1;i<len;i++){
            while(!queue.isEmpty()&&nums[i]>nums[queue.getLast()]){
                queue.removeLast();
            }
            while(!queue.isEmpty()&&i-queue.getFirst()>=k){
                queue.remove();
            }
            queue.add(i);
            res[i-k+1]=nums[queue.getFirst()];
        }
        return res;
    }
}

 

posted @ 2015-08-08 20:51  ~每天进步一点点~  阅读(197)  评论(0编辑  收藏  举报