题目要求

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.

假设有一个数组和一个长度为k的窗口,1 ≤ k ≤ 数组长度。这个窗口每次向右移动一位。现在问该窗口在各个位置上,能够得到的子数组的最大值是多少?

思路与代码

如果直接使用TreeSet会有问题,因为Set遇到重复的值时,只会将其添加一次。比如下面这个数组[3,2,3],1。当窗口右滑时,会删除下标0上的值,并加入下标3上的值。此时Set中记录的值编程了[2,1],并返回当前的最大值为2。但是明显下标为2上的值也是3。所以我们不能直接使用Set解决问题。

但是如果每次我们都重新计算滑动窗口中的最大值,那明显浪费了很多之前遍历所提供的有效的信息。我们可以试着去看每次遍历能够得到什么可以重复利用的信息从而尽可能减去无效的遍历。

就看题目中的例子,[1 3 -1] -3 5 3 6 7,我们知道这个窗口中的最大值为3。我们同时也可以确定,3之前的数字无需加入后面大小的比较,因为它们一定比3小。

按照这种规则,我们可以维护一个存储了可比较数字的链表。这个链表中的数字可以和当前准备加入链表的值进行比较。那么我们看一下将一个值加入该链表有什么场景:

  1. 链表为空,直接加入
  2. 链表的数量大于窗口,则删除最左侧的值
  3. 链表中有值,且有些值小于即将加入的值,则这些小于的值都被抛弃
  4. 链表中的值均大于即将加入的值,则不进行任何操作

通过这种方式,我们确保了链表头的值一定是当前窗口的最大值。

    public int[] maxSlidingWindow(int[] nums, int k) {
        if(nums.length == 0 || k == 0) return new int[0];
        LinkedList<Integer> list = new LinkedList<Integer>();
        int[] result = new int[nums.length - k + 1];
        
        for(int i = 0 ; i<nums.length ; i++){
            while(list.size() != 0 && list.getFirst() < i-k+1){
                list.remove(0);
            }
            while(list.size()!=0 && nums[list.getLast()] < nums[i]){
                list.remove(list.size()-1);
            }
            list.addLast(i);
            if(i - k + 1 >= 0){
                result[i-k+1] = nums[list.getFirst()];
            }
        }
        return result;
    }

当然,我们也可以不使用链表来存储当前可比较最大值,而是直接存储当前最大值的下标。一旦最大值失效,就从窗口中重新找一个最大值就好了。而如果当前值比之前的最大值更大,则将最大值下标更新为当前下标就好了。

    public int[] maxSlidingWindow_noDataStructure(int[] nums, int k) {
        if(nums.length == 0 || k == 0) return new int[0];
        int[] result = new int[nums.length - k + 1];
        int max = 0;
        for(int i = 0 ; i<nums.length ; i++){
            if(max < i - k + 1){
                max = i;
                for(int j = 1 ; j<k ; j++){
                    if(nums[i-j] > nums[max]){
                        max = i-j;
                    }
                }
            }
            if(nums[i] > nums[max]) max = i;
            if(i-k+1>=0){
                result[i-k+1] = nums[max];
            }
        }
        return result;
    }

clipboard.png
想要了解更多开发技术,面试教程以及互联网公司内推,欢迎关注我的微信公众号!将会不定期的发放福利哦~


raledong
2.7k 声望2k 粉丝

心怀远方,负重前行