239. 滑动窗口最大值

image

239. 滑动窗口最大值

思路

  1. 双端队列的使用:我们使用双端队列来存储可能成为窗口最大值的元素索引。队列中的元素按从大到小排序。

  2. 维护队列

    • 移除不在当前窗口范围内的元素(从队列头部移除)
    • 移除所有小于当前元素的队列元素(从队列尾部移除),因为它们不可能是当前或未来窗口的最大值
    • 将当前元素添加到队列尾部
  3. 记录结果:当窗口完全形成时(即i >= k-1),队列头部的元素就是当前窗口的最大值。

遍历时,i从0开始,作为窗口的最右端,窗口区间为[i-k+1, i],当第一个完整窗口索引为[0, k-1],所以当i >= k-1时开始取队列中的最大值。

结果数组的长度,取决于,第一个完整窗口移动到最右端的次数,即从k-1移动到n-1,再加上第一个窗口的最大值个数,n-1-(k-1)+1=n-k+1

复杂度分析

  • 时间复杂度:O(n),每个元素被处理两次(一次添加,一次移除)
  • 空间复杂度:O(k),双端队列最多存储k个元素

代码

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || nums.length == 0 || k <= 0) {
            return new int[0];
        }

        int n = nums.length;
        int[] res = new int[n - k + 1];
        int resi = 0;
        // 优先队列,用来保存可能是窗口最大值的元素索引
        Deque<Integer> deque = new LinkedList<>();

        for (int i = 0; i < n; i++) {
            // 移出不在窗口内的索引,最先加入的最先移出,从头部开始
            while (!deque.isEmpty() && deque.peekFirst() < i - k + 1) {
                deque.pollFirst();
            }

            // 移出小于当前值的元素索引,因为该值不可能是当前窗口或未来窗口的最大值
            while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
                // 为了保证队列中索引对应的元素是从大到小排列,从尾部开始
                deque.pollLast();
            }

            // 添加当前元素索引到尾部
            deque.offerLast(i);

            // 当形成完整的窗口后,开始取最大值
            if (i >= k-1) {
                res[resi++] = nums[deque.peekFirst()];
            }
        }

        return res;
    }
}

posted @ 2025-08-09 00:37  quanht  阅读(11)  评论(0)    收藏  举报