LeetCode题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. Return the max sliding window.

Example:

Input: nums = [1,3,-1,-3,5,3,6,7], and k = 3
Output: [3,3,5,5,6,7] 
Explanation: 

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

Note: 
You may assume k is always valid, 1 ≤ k ≤ input array's size for non-empty array.

Follow up:
Could you solve it in linear time?

思路

咋一看不知道题目要干嘛,其实求的是窗口从左往右滑动过程中,返回窗口中元素的最大值。比如上面Max的那一列是 3 3 5 5 6 7,那么返回的就是这个。

感觉直接遍历好像就可以了诶,不过应该会超时。而且提示我们用O(n)复杂度内解题了,看了下别人的解法,使用的是双向队列deque,保存数组元素的索引,遍历整个数组。

We scan the array from 0 to n-1, keep "promising" elements in the deque. The algorithm is amortized O(n) as each element is put and polled once.

At each i, we keep "promising" elements, which are potentially max number in window [i-(k-1),i] or any subsequent window. This means

  1.  If an element in the deque and it is out of i-(k-1), we discard them. We just need to poll from the head, as we are using a deque and elements are ordered as the sequence in the array

  2.  Now only those elements within [i-(k-1),i] are in the deque. We then discard elements smaller than a[i] from the tail. This is because if a[x] <a[i] and x<i, then a[x] has no chance to be the "max" in [i-(k-1),i], or any other subsequent window: a[i] would always be a better candidate.

  3.  As a result elements in the deque are ordered in both sequence in array and their value. At each step the head of the deque is the max element in [i-(k-1),i]

对于每个索引i位置,它都有可能是窗口[i-(k-1),i]中的那个最大值,以题目中的例子来说明,假如i=4, 那么窗口[2, 4],即索引 2,3,4位置构成的窗口。位置4可能是这个窗口中的最大值,或者是后面窗口的最大值(因为向右滑动时还包含在里面)。这表明:

1. 如果当前队列中的索引不在窗口[2, 4]的范围内,那么丢弃它。

2. 如果现在队列中的索引只有当前窗口里面[i-(k-1),i]的,那么从尾部开始丢弃掉比a[i]小的元素,这是因为如果 a[x]<a[i] 并且 x<i,那么a[x] 不可能是 [i-(k-1), i] 中的最大值,并且在后面的窗口中也不可能是最大的,因为a[i]是更好的选择。(因为当前的索引,也就是从 i-(k-1)到i都被限制在一个窗口里了)

3. 因为每次的滑动窗口所选择的出的最大元素在队列中是按照他们的数组索引和值排序的,所以每一不这个deque的头就是 [i-(k-1),i] 中的最大元素。

代码

public int[] maxSlidingWindow(int[] a, int k) {        
        if (a == null || k <= 0) {
            return new int[0];
        }
        int n = a.length;
        int[] r = new int[n-k+1];
        int ri = 0;
        // store index
        Deque<Integer> q = new ArrayDeque<>(); //双端队列的用法
        for (int i = 0; i < a.length; i++) {
            // 将不在当前窗口 i-k+1 到 i 内的索引移除
            while (!q.isEmpty() && q.peek() < i - k + 1) { 
                q.poll(); // 弹出队列中的第一元素(头部,最左边)
            }
            // 从尾部(队列右边)remove smaller numbers in k range as they are useless
            while (!q.isEmpty() && a[q.peekLast()] < a[i]) {
                q.pollLast();
            }
            // q contains index... r contains content
            q.offer(i);
            if (i >= k - 1) {  // 只需要判断i-k+1>0即可,因为只有一开始不够k个元素
                r[ri++] = a[q.peek()]; // 经过上面处理,队头是当前窗口中的最大元素
            }
        }
        return r;
    }

猜你喜欢

转载自www.cnblogs.com/f91og/p/9465799.html