【算法入门】LeetCode 239. 滑动窗口最大值:Java与JavaScript双解法详解|单调队列的精妙运用力扣239题详解:滑动窗口最大值(Java & JavaScript 双语言实现)

题目:

在这里插入图片描述

官方链接:

https://leetcode.cn/problems/sliding-window-maximum/description/?envType=study-plan-v2&envId=top-100-liked

参考答案:

【新手入门】LeetCode 239. 滑动窗口最大值:Java & JavaScript 双解法详解

目录

  1. 题目描述
  2. 问题分析
  3. 解题思路
    • 3.1 暴力法(不推荐)
    • 3.2 单调队列法(最优解)
  4. Java代码实现
  5. JavaScript代码实现
  6. 复杂度分析
  7. 边界条件与注意事项
  8. 总结

1. 题目描述

给定一个整数数组 nums 和一个整数 k,有一个大小为 k 的滑动窗口从数组的最左侧移动到最右侧。每次滑动窗口向右移动一位,返回每个滑动窗口中的最大值。

示例 1

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]

示例 2

输入:nums = [1], k = 1
输出:[1]

2. 问题分析

  • 滑动窗口:窗口每次右移一位,需要高效计算窗口内的最大值。
  • 性能要求
    • 暴力法时间复杂度为 O(nk),无法通过大规模数据测试(n ≤ 10^5)。
    • 需要优化到 O(n) 时间复杂度。
  • 核心挑战:如何在窗口滑动时高效维护最大值。

3. 解题思路

3.1 暴力法(不推荐)

  • 思路:对每个窗口遍历所有元素找最大值。
  • 缺点:时间复杂度 O(nk),无法通过测试。

3.2 单调队列法(最优解)

核心思想

  • 使用 双端队列(Deque) 存储当前窗口内可能成为最大值的元素的索引。
  • 队列保持单调递减(队首始终是当前窗口的最大值)。

操作流程

  1. 初始化队列:处理前 k 个元素,维护单调递减队列。
  2. 滑动窗口
    • 移除队列中不在窗口范围内的索引(从队首开始检查)。
    • 移除队列中比当前元素小的索引(从队尾开始检查)。
    • 将当前元素索引加入队尾。
  3. 记录最大值:每次窗口移动后,队首元素即为当前窗口的最大值。

优势

  • 每个元素最多入队和出队一次,时间复杂度 O(n)。
  • 空间复杂度 O(k)(队列最多存储 k 个元素)。

4. Java代码实现

import java.util.*;

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[] result = new int[n - k + 1];
        Deque<Integer> deque = new ArrayDeque<>();
        
        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) {
                result[i - k + 1] = nums[deque.peekFirst()];
            }
        }
        
        return result;
    }
}

5. JavaScript代码实现

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number[]}
 */
var maxSlidingWindow = function(nums, k) {
    if (!nums.length || k === 0) return [];
    
    const n = nums.length;
    const result = [];
    const deque = [];
    
    for (let i = 0; i < n; i++) {
        // 移除不在窗口范围内的索引
        while (deque.length > 0 && deque[0] < i - k + 1) {
            deque.shift();
        }
        
        // 移除比当前元素小的索引
        while (deque.length > 0 && nums[deque[deque.length - 1]] < nums[i]) {
            deque.pop();
        }
        
        // 加入当前索引
        deque.push(i);
        
        // 记录窗口最大值
        if (i >= k - 1) {
            result.push(nums[deque[0]]);
        }
    }
    
    return result;
};

6. 复杂度分析

方法时间复杂度空间复杂度
暴力法O(nk)O(1)
单调队列法O(n)O(k)

7. 边界条件与注意事项

  1. 空数组或 k=0:直接返回空数组。
  2. k=1:每个窗口只有一个元素,直接返回原数组。
  3. k ≥ nums.length:整个数组就是一个窗口,返回全局最大值。
  4. 队列操作
    • Java 使用 Deque 接口的实现类 ArrayDeque
    • JavaScript 使用数组模拟双端队列(注意 shift()pop() 的性能)。

8. 总结

  • 面试推荐:单调队列法是标准解法,考察对数据结构的灵活运用。
  • 核心技巧
    1. 使用双端队列维护窗口内的可能最大值。
    2. 队首始终是当前窗口的最大值。
    3. 及时移除无效索引(不在窗口内或比当前元素小)。
  • 适用场景:需要高效计算滑动窗口最值的场景(如股票分析、实时数据流)。

掌握这道题,你就能高效解决滑动窗口最大值问题! 🚀


博客标题:滑动窗口最大值问题:Java与JavaScript解决方案


目录
  1. 引言
  2. 问题分析
  3. Java实现
    1. 算法思路
    2. 代码实现
  4. JavaScript实现
    1. 算法思路
    2. 代码实现
  5. 总结

1. 引言

滑动窗口最大值问题是算法中的一个常见问题,它要求我们在一个固定大小的窗口中找到最大值。在每次窗口滑动时,我们需要更新最大值。本文将分别使用Java和JavaScript来解答这个问题。

2. 问题分析

给定一个整数数组 nums 和一个整数 k,表示窗口的大小。我们需要计算每个窗口中的最大值,并返回一个包含所有窗口最大值的数组。

3. Java实现

3.1 算法思路

我们可以使用一个双端队列(Deque)来维护窗口中的最大值。具体步骤如下:

  1. 初始化一个双端队列 deque,它将存储窗口中的元素索引。
  2. 遍历数组 nums
    • 对于每个元素,首先从队列的尾部移除所有小于当前元素的值,因为这些值不可能是后续窗口的最大值。
    • 将当前元素的索引添加到队列的尾部。
    • 如果队列的头部索引超出了当前窗口的范围,则将其移除。
    • 每次迭代时,队列的头部存储的是当前窗口的最大值的索引。
  3. 返回一个数组,其中包含每个窗口的最大值。
3.2 代码实现
import java.util.ArrayDeque;
import java.util.Deque;

public class MaxSlidingWindow {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || nums.length == 0 || k <= 0) {
            return new int[0];
        }
        
        Deque<Integer> deque = new ArrayDeque<>();
        int[] result = new int[nums.length - k + 1];
        
        for (int i = 0; i < nums.length; i++) {
            // 移除不在窗口内的元素
            if (!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) {
                result[i - k + 1] = nums[deque.peekFirst()];
            }
        }
        
        return result;
    }
}

4. JavaScript实现

4.1 算法思路

JavaScript的实现与Java类似,只是语法上有所不同。

4.2 代码实现
function maxSlidingWindow(nums, k) {
    if (!nums || nums.length === 0 || k <= 0) {
        return [];
    }
    
    let deque = [];
    let result = [];
    
    for (let i = 0; i < nums.length; i++) {
        // 移除不在窗口内的元素
        if (deque.length && deque[0] < i - k + 1) {
            deque.shift();
        }
        
        // 移除小于当前元素的值
        while (deque.length && nums[deque[deque.length - 1]] < nums[i]) {
            deque.pop();
        }
        
        // 添加当前元素的索引
        deque.push(i);
        
        // 如果窗口已满,则记录最大值
        if (i >= k - 1) {
            result.push(nums[deque[0]]);
        }
    }
    
    return result;
}

5. 总结

通过本文,我们学习了如何使用Java和JavaScript解决滑动窗口最大值问题。使用双端队列来维护窗口中的最大值是一种有效的方法,它允许我们在O(n)的时间复杂度内完成整个数组的遍历。希望这篇文章能帮助你更好地理解并掌握这一知识点。


好的,我很乐意为你提供详细的解答和博客内容。以下是一篇新手入门博客,包含Java和JavaScript的解答以及相关知识点的详细介绍。

博客标题: 从零开始学习力扣题 239. 滑动窗口最大值

目录:

  1. 问题描述

  2. Java解答
    2.1 使用双端队列

  3. JavaScript解答
    3.1 使用双端队列

  4. 知识点总结
    4.1 滑动窗口
    4.2 双端队列
    4.3 时间复杂度和空间复杂度

  5. 问题描述
    力扣题 239. 滑动窗口最大值要求实现一个函数 maxSlidingWindow(nums, k),其中 nums 是一个整数数组, k 是滑动窗口的大小。该函数需要返回滑动窗口中的最大值。

  6. Java解答

2.1 使用双端队列

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

        int n = nums.length;
        int[] result = new int[n - k + 1];
        Deque<Integer> deque = new LinkedList<>();

        for (int i = 0; i < n; i++) {
            // 移除队列中小于当前元素的值
            while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
                deque.pollLast();
            }
            // 添加当前元素索引
            deque.offerLast(i);

            // 移除队列中超出滑动窗口的元素
            if (deque.peekFirst() == i - k) {
                deque.pollFirst();
            }

            // 当滑动窗口大小达到 k 时,将最大值添加到结果数组
            if (i >= k - 1) {
                result[i - k + 1] = nums[deque.peekFirst()];
            }
        }

        return result;
    }
}
  1. JavaScript解答

3.1 使用双端队列

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number[]}
 */
var maxSlidingWindow = function(nums, k) {
    if (!nums || nums.length === 0) {
        return [];
    }

    const n = nums.length;
    const result = [];
    const deque = [];

    for (let i = 0; i < n; i++) {
        // 移除队列中小于当前元素的值
        while (deque.length > 0 && nums[deque[deque.length - 1]] < nums[i]) {
            deque.pop();
        }
        // 添加当前元素索引
        deque.push(i);

        // 移除队列中超出滑动窗口的元素
        if (deque[0] === i - k) {
            deque.shift();
        }

        // 当滑动窗口大小达到 k 时,将最大值添加到结果数组
        if (i >= k - 1) {
            result.push(nums[deque[0]]);
        }
    }

    return result;
};
  1. 知识点总结

4.1 滑动窗口
滑动窗口是一种常见的数据结构,它可以用来解决一些需要维护一个固定大小窗口的问题,比如求最大值、最小值等。

4.2 双端队列
双端队列是一种特殊的队列,它允许在队列的两端进行插入和删除操作。在本题中,我们使用双端队列来维护滑动窗口中的最大值。

4.3 时间复杂度和空间复杂度

  • maxSlidingWindow() 方法的时间复杂度为 O(n),空间复杂度为 O(k),其中 n 是数组的长度,k 是滑动窗口的大小。

滑动窗口最大值:Java与JavaScript详解(LeetCode 239)

目录

  1. 引言
  2. 题目简介
  3. 解题思路
  4. Java实现详解
  5. JavaScript实现详解
  6. 其他优化技巧
  7. 结语

1. 引言

在处理连续区间最大值的问题时,“滑动窗口最大值”是经典也是非常实用的一题。它不仅考察你的数据结构基础,还锻炼你的算法优化能力。本文将逐步讲解此题的思路,并用Java和JavaScript两种语言完整实现,帮助你理解和掌握解决方案。


2. 题目简介

题目描述:
给定一个数组nums和一个滑动窗口大小k,窗口在数组上从左到右滑动,每次移动一格。每次滑动后,求窗口内的最大值。

示例:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]

详细解释:
窗口位置                  最大值
[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

提示:

  • 1 <= nums.length <= 10^5
  • -10^4 <= nums[i] <= 10^4
  • 1 <= k <= nums.length

3. 解题思路

方法一:使用双端队列(核心方案)

利用一个双端队列(Deque)存储当前窗口可能的最大值的索引:

  • 保持队列索引对应的值按降序排列,队首为最大值。
  • 当滑动窗口向右移动:
    • 弹出队尾中所有比新元素小的索引(因为它们不可能成为最大值)
    • 将新元素索引加入队尾
    • 如果队首元素已经超出窗口范围(索引小于 i - k + 1),就弹出队首
    • 当前窗口最大值即队首元素对应的值

优点:

  • 时间复杂度:O(n),每个元素最多入队出队一次。

4. Java实现详解

import java.util.Deque;
import java.util.LinkedList;

public class SlidingWindowMaximum {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || k == 0) return new int[0];
        int n = nums.length;
        int[] result = new int[n - k + 1];
        Deque<Integer> deque = new LinkedList<>();
        for (int i = 0; i < n; i++) {
            // 移除队尾所有比当前元素小的索引
            while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
                deque.pollLast();
            }
            deque.offerLast(i);
            // 移除超出窗口范围的索引
            if (deque.peekFirst() == i - k) {
                deque.pollFirst();
            }
            // 收集结果
            if (i >= k - 1) {
                result[i - k + 1] = nums[deque.peekFirst()];
            }
        }
        return result;
    }

    public static void main(String[] args) {
        SlidingWindowMaximum solution = new SlidingWindowMaximum();
        int[] nums = {1,3,-1,-3,5,3,6,7};
        int[] res = solution.maxSlidingWindow(nums, 3);
        for (int v : res) {
            System.out.print(v + " ");
        }
        // 输出:3 3 5 5 6 7
    }
}

5. JavaScript实现详解

var maxSlidingWindow = function(nums, k) {
    const result = [];
    const deque = []; // 存索引
    for (let i = 0; i < nums.length; i++) {
        // 移除队尾所有比当前元素小的索引
        while (deque.length && nums[deque[deque.length -1]] < nums[i]) {
            deque.pop();
        }
        deque.push(i);
        // 移除超出窗口范围的索引
        if (deque[0] === i - k) {
            deque.shift();
        }
        // 收集结果
        if (i >= k -1) {
            result.push(nums[deque[0]]);
        }
    }
    return result;
};

// 示例测试
console.log(maxSlidingWindow([1,3,-1,-3,5,3,6,7], 3)); // [3,3,5,5,6,7]

6. 其他优化技巧

  • 用单调队列:保证队列中的元素单调递减,提高效率。
  • 不同数据结构选择:优先队列、平衡树等也可以实现,但双端队列最简洁高效。

7. 结语

利用双端队列(队列的单调性特点)是解决“滑动窗口最大值”问题的标准方案。这种技巧不仅在此题中有用,还可以应用到诸如滑动窗口平均值、区间判定等问题中。理解和掌握这种数据结构的用法,将极大增强你的算法能力。

祝你练习顺利,算法之路越走越宽!


力扣题解:239. 滑动窗口最大值

目录

  1. 题目描述
  2. 解题思路
    • 2.1 暴力解法
    • 2.2 双端队列解法
  3. Java 实现
  4. JavaScript 实现
  5. 总结

1. 题目描述

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回滑动窗口中的最大值。

示例

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]

提示

  • (1 \leq \text{nums.length} \leq 10^5)
  • (-10^4 \leq \text{nums[i]} \leq 10^4)
  • (1 \leq k \leq \text{nums.length})

2. 解题思路

2.1 暴力解法

暴力解法的思路是,对于每一个滑动窗口,遍历窗口内的元素,找到最大值。这个方法的时间复杂度为 (O(n \cdot k)),在最坏情况下可能会超时。

2.2 双端队列解法

为了提高效率,我们可以使用双端队列(Deque)来维护当前滑动窗口的最大值。具体步骤如下:

  1. 维护一个双端队列:队列中的元素是数组的索引,且队列中的元素是按值从大到小排列的。
  2. 滑动窗口的移动
    • 在每次移动窗口时,首先移除队列中不在当前窗口的元素。
    • 然后,移除队列中所有小于当前元素的索引(因为它们不可能是最大值)。
    • 将当前元素的索引添加到队列中。
    • 当窗口的大小达到 k 时,队列的头部就是当前窗口的最大值。

这种方法的时间复杂度为 (O(n)),因为每个元素最多被添加和删除一次。


3. Java 实现

以下是 Java 的实现代码:

import java.util.ArrayDeque;
import java.util.Deque;

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || nums.length == 0) return new int[0];
        
        int n = nums.length;
        int[] result = new int[n - k + 1];
        Deque<Integer> deque = new ArrayDeque<>();
        
        for (int i = 0; i < n; i++) {
            // Remove elements not in the sliding window
            if (!deque.isEmpty() && deque.peek() < i - k + 1) {
                deque.poll();
            }
            
            // Remove elements smaller than the current element
            while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
                deque.pollLast();
            }
            
            // Add current element's index
            deque.offer(i);
            
            // Add the maximum for the current window
            if (i >= k - 1) {
                result[i - k + 1] = nums[deque.peek()];
            }
        }
        
        return result;
    }
}

4. JavaScript 实现

以下是 JavaScript 的实现代码:

var maxSlidingWindow = function(nums, k) {
    if (nums.length === 0) return [];
    
    const result = [];
    const deque = []; // 存储索引
    
    for (let i = 0; i < nums.length; i++) {
        // 移除不在滑动窗口的元素
        if (deque.length && deque[0] < i - k + 1) {
            deque.shift();
        }
        
        // 移除所有小于当前元素的索引
        while (deque.length && nums[deque[deque.length - 1]] < nums[i]) {
            deque.pop();
        }
        
        // 添加当前元素的索引
        deque.push(i);
        
        // 添加当前窗口的最大值
        if (i >= k - 1) {
            result.push(nums[deque[0]]);
        }
    }
    
    return result;
};

5. 总结

在这篇博客中,我们讨论了力扣题目“239. 滑动窗口最大值”的解法。我们首先介绍了题目的基本要求,然后分析了使用双端队列的方法来高效地找到滑动窗口中的最大值。最后,我们提供了 Java 和 JavaScript 的实现代码。

通过这种方法,我们能够在 (O(n)) 的时间复杂度内处理数据流,适合处理较大的输入数据。希望这篇博客能帮助你更好地理解这个问题及其解法!如果你有任何问题或想法,欢迎在评论区留言讨论。


好的,为你准备一篇关于 LeetCode 239. 滑动窗口最大值的详细入门博客,包含 Java 和 JavaScript 的解法,并深入讲解算法思路和复杂度分析。

博客标题: LeetCode 239. 滑动窗口最大值:新手入门指南 (Java & JavaScript)

目录:

  1. 引言:滑动窗口中的最大值
  2. 题目描述
  3. 解题思路:单调队列
    • 3.1 为什么选择单调队列?
    • 3.2 算法步骤
  4. Java 代码实现
    • 4.1 代码
    • 4.2 代码解释
  5. JavaScript 代码实现
    • 5.1 代码
    • 5.2 代码解释
  6. 复杂度分析
    • 6.1 时间复杂度
    • 6.2 空间复杂度
  7. 总结:掌握单调队列,高效解决滑动窗口问题!

博客正文:

1. 引言:滑动窗口中的最大值

“滑动窗口最大值”是 LeetCode 上一道经典的困难难度题目。它考察了你对队列这种数据结构的理解和应用,以及你解决问题的能力。掌握这道题的解法,对于你提升算法思维非常有帮助。

2. 题目描述

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值

示例:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[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

输入:nums = [1], k = 1
输出:[1]

3. 解题思路:单调队列

  • 3.1 为什么选择单调队列?

    • 高效维护最大值: 单调队列可以高效地维护滑动窗口中的最大值。
    • 动态调整: 单调队列可以在 O(1) 的时间复杂度内插入和删除元素,这使得它非常适合处理滑动窗口问题。
    • 避免重复比较: 单调队列可以避免重复比较,从而提高效率。
  • 3.2 算法步骤

    1. 创建一个双端队列(deque),用于存储数组的索引。
    2. 遍历数组 nums
    3. 对于每个元素 nums[i]
      • 将队列中所有小于 nums[i] 的元素都移除。 这是为了保证队列是单调递减的。
      • i 添加到队列的末尾。
      • 如果队列的头部元素已经不在滑动窗口中(即 deque.peekFirst() < i - k + 1),将队列的头部元素移除。
      • 如果 i >= k - 1,将队列的头部元素(即当前滑动窗口的最大值的索引)对应的 nums[deque.peekFirst()] 添加到结果数组中。
    4. 返回结果数组。

4. Java 代码实现

  • 4.1 代码

    import java.util.Deque;
    import java.util.LinkedList;
    
    class Solution {
        public int[] maxSlidingWindow(int[] nums, int k) {
            int n = nums.length;
            if (n * k == 0) return new int[0];
            if (k == 1) return nums;
    
            Deque<Integer> deque = new LinkedList<>();
            int[] result = new int[n - k + 1];
    
            for (int i = 0; i < n; i++) {
                // 移除队列中小于当前元素的元素
                while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
                    deque.removeLast();
                }
    
                // 添加当前元素的索引到队列
                deque.addLast(i);
    
                // 移除队列中不在滑动窗口中的元素
                if (deque.peekFirst() < i - k + 1) {
                    deque.removeFirst();
                }
    
                // 如果滑动窗口已经形成,将最大值添加到结果数组
                if (i >= k - 1) {
                    result[i - k + 1] = nums[deque.peekFirst()];
                }
            }
            return result;
        }
    }
    
  • 4.2 代码解释

    • Deque<Integer> deque = new LinkedList<>();: 创建一个双端队列。
    • while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) { deque.removeLast(); }: 移除队列中小于当前元素的元素。
    • deque.addLast(i);: 添加当前元素的索引到队列。
    • if (deque.peekFirst() < i - k + 1) { deque.removeFirst(); }: 移除队列中不在滑动窗口中的元素。
    • if (i >= k - 1) { result[i - k + 1] = nums[deque.peekFirst()]; }: 如果滑动窗口已经形成,将最大值添加到结果数组。

5. JavaScript 代码实现

  • 5.1 代码

    function maxSlidingWindow(nums, k) {
        const n = nums.length;
        if (n * k === 0) return [];
        if (k === 1) return nums;
    
        const deque = [];
        const result = new Array(n - k + 1);
    
        for (let i = 0; i < n; i++) {
            // 移除队列中小于当前元素的元素
            while (deque.length > 0 && nums[deque[deque.length - 1]] < nums[i]) {
                deque.pop();
            }
    
            // 添加当前元素的索引到队列
            deque.push(i);
    
            // 移除队列中不在滑动窗口中的元素
            if (deque[0] < i - k + 1) {
                deque.shift();
            }
    
            // 如果滑动窗口已经形成,将最大值添加到结果数组
            if (i >= k - 1) {
                result[i - k + 1] = nums[deque[0]];
            }
        }
        return result;
    }
    
  • 5.2 代码解释

    • const deque = [];: 创建一个双端队列。
    • while (deque.length > 0 && nums[deque[deque.length - 1]] < nums[i]) { deque.pop(); }: 移除队列中小于当前元素的元素。
    • deque.push(i);: 添加当前元素的索引到队列。
    • if (deque[0] < i - k + 1) { deque.shift(); }: 移除队列中不在滑动窗口中的元素。
    • if (i >= k - 1) { result[i - k + 1] = nums[deque[0]]; }: 如果滑动窗口已经形成,将最大值添加到结果数组。

6. 复杂度分析

  • 6.1 时间复杂度

    • O(n),其中 n 是数组的长度。虽然有一个 while 循环,但是每个元素最多只会被添加到队列一次,最多只会被移除队列一次。
  • 6.2 空间复杂度

    • O(k),需要使用一个双端队列来存储滑动窗口中的元素。

7. 总结:掌握单调队列,高效解决滑动窗口问题!

“滑动窗口最大值”是一道经典的算法题,通过学习这道题,你可以掌握单调队列这种重要的数据结构,并学会如何使用它来解决实际问题。 希望这篇博客能帮助你入门算法和数据结构,并在你的编程之路上助你一臂之力! 继续学习和实践,你一定会成为一名优秀的程序员!



力扣 239 题:滑动窗口最大值新手入门攻略

一、引言

在算法学习的道路上,滑动窗口问题是一类经典且常见的问题。力扣第 239 题“滑动窗口最大值”就是这类问题中的一个典型代表。本题要求我们在一个整数数组中,找出每个固定大小的滑动窗口内的最大值。接下来,我们将详细分析这道题,并分别给出 Java 和 JavaScript 的实现方案。

二、题目分析

2.1 题目描述

给定一个整数数组 nums 和一个大小为 k 的滑动窗口,该窗口从数组的最左侧移动到最右侧,每次只向右移动一位。我们需要返回每个滑动窗口内的最大值。

2.2 示例分析

  • 示例 1
    • 输入:nums = [1,3,-1,-3,5,3,6,7]k = 3
    • 输出:[3,3,5,5,6,7]
    • 解释:
      | 滑动窗口的位置 | 最大值 |
      | — | — |
      | [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 |
  • 示例 2
    • 输入:nums = [1]k = 1
    • 输出:[1]

2.3 提示信息

  • 1 <= nums.length <= 10^5
  • -10^4 <= nums[i] <= 10^4
  • 1 <= k <= nums.length

三、解题思路

为了高效地解决这个问题,我们可以使用单调队列。单调队列是一种特殊的数据结构,它可以在 O ( 1 ) O(1) O(1) 的时间复杂度内获取队列中的最大值。具体步骤如下:

  1. 初始化一个双端队列 deque,用于存储数组元素的下标。
  2. 遍历数组 nums
    • 当队列不为空且队列尾部元素对应的数组值小于当前元素时,不断移除队列尾部元素,以保证队列的单调性。
    • 将当前元素的下标加入队列尾部。
    • 如果队列头部元素的下标已经超出了当前滑动窗口的范围,移除队列头部元素。
    • 当遍历到第 k - 1 个元素及以后时,队列头部元素对应的数组值就是当前滑动窗口的最大值,将其加入结果数组。

四、Java 实现

import java.util.ArrayDeque;
import java.util.Deque;

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[] result = new int[n - k + 1];
        // 双端队列,存储数组元素的下标
        Deque<Integer> deque = new ArrayDeque<>();

        for (int i = 0; i < n; i++) {
            // 移除队列尾部小于当前元素的元素
            while (!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {
                deque.pollLast();
            }
            // 将当前元素的下标加入队列尾部
            deque.offerLast(i);
            // 移除队列头部超出滑动窗口范围的元素
            if (deque.peekFirst() <= i - k) {
                deque.pollFirst();
            }
            // 当遍历到第 k - 1 个元素及以后时,记录当前滑动窗口的最大值
            if (i >= k - 1) {
                result[i - k + 1] = nums[deque.peekFirst()];
            }
        }
        return result;
    }

    public static void main(String[] args) {
        Solution solution = new Solution();
        int[] nums = {1, 3, -1, -3, 5, 3, 6, 7};
        int k = 3;
        int[] result = solution.maxSlidingWindow(nums, k);
        for (int num : result) {
            System.out.print(num + " ");
        }
    }
}

4.1 代码解释

  • deque 是一个双端队列,用于存储数组元素的下标。
  • 在遍历数组时,通过 while 循环移除队列尾部小于当前元素的元素,保证队列的单调性。
  • 当队列头部元素的下标超出滑动窗口范围时,使用 pollFirst 方法移除队列头部元素。
  • 当遍历到第 k - 1 个元素及以后时,将队列头部元素对应的数组值加入结果数组。

4.2 复杂度分析

  • 时间复杂度 O ( n ) O(n) O(n),其中 n n n 是数组的长度。每个元素最多进队和出队一次。
  • 空间复杂度 O ( k ) O(k) O(k),主要用于存储队列中的元素,队列中最多存储 k k k 个元素。

五、JavaScript 实现

var maxSlidingWindow = function(nums, k) {
    if (nums.length === 0 || k <= 0) {
        return [];
    }
    const n = nums.length;
    const result = [];
    // 双端队列,存储数组元素的下标
    const deque = [];

    for (let i = 0; i < n; i++) {
        // 移除队列尾部小于当前元素的元素
        while (deque.length > 0 && nums[deque[deque.length - 1]] < nums[i]) {
            deque.pop();
        }
        // 将当前元素的下标加入队列尾部
        deque.push(i);
        // 移除队列头部超出滑动窗口范围的元素
        if (deque[0] <= i - k) {
            deque.shift();
        }
        // 当遍历到第 k - 1 个元素及以后时,记录当前滑动窗口的最大值
        if (i >= k - 1) {
            result.push(nums[deque[0]]);
        }
    }
    return result;
};

// 测试代码
const nums = [1, 3, -1, -3, 5, 3, 6, 7];
const k = 3;
const result = maxSlidingWindow(nums, k);
console.log(result);

5.1 代码解释

  • deque 是一个数组,用于模拟双端队列,存储数组元素的下标。
  • 使用 while 循环移除队列尾部小于当前元素的元素,保证队列的单调性。
  • 当队列头部元素的下标超出滑动窗口范围时,使用 shift 方法移除队列头部元素。
  • 当遍历到第 k - 1 个元素及以后时,将队列头部元素对应的数组值加入结果数组。

5.2 复杂度分析

  • 时间复杂度 O ( n ) O(n) O(n),其中 n n n 是数组的长度。每个元素最多进队和出队一次。
  • 空间复杂度 O ( k ) O(k) O(k),主要用于存储队列中的元素,队列中最多存储 k k k 个元素。

六、总结

本题通过使用单调队列的方法,高效地解决了滑动窗口最大值的问题。在 Java 中,我们可以使用 ArrayDeque 来实现双端队列;在 JavaScript 中,我们可以使用数组来模拟双端队列。两种实现的时间复杂度和空间复杂度相同,都能满足题目的要求。希望这篇博客能帮助新手朋友们更好地理解和解决这道题目。

七、参考资料

  • 力扣(LeetCode)官方网站
  • 《算法导论》相关章节

【新手入门】LeetCode 239. 滑动窗口最大值:Java & JavaScript双语言详解

目录

  1. 题目描述
  2. 核心概念解析
  3. 解题思路分析
  4. Java实现
  5. JavaScript实现
  6. 复杂度分析
  7. 总结与扩展

1. 题目描述

给定一个整数数组 nums 和一个整数 k,有一个大小为 k 的滑动窗口从数组的最左侧移动到最右侧。每次滑动窗口向右移动一位,返回每次滑动窗口中的最大值。

示例 1:

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:
滑动窗口的位置                最大值
---------------               -----
[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

示例 2:

输入:nums = [1], k = 1
输出:[1]

2. 核心概念解析

什么是滑动窗口?

  • 滑动窗口是一种在数组或字符串上移动的固定大小的子数组/子串。
  • 每次窗口向右移动一位,直到覆盖整个数组。

如何高效求窗口最大值?

  • 暴力法:每次计算窗口内的最大值,时间复杂度 O(nk),不适用于大数据量。
  • 优化方法:使用双端队列(Deque),保证队列头部始终是当前窗口的最大值,时间复杂度 O(n)。

3. 解题思路分析

方法:双端队列(Deque)

  1. 维护一个双端队列,存储数组的索引(而不是值),队列中的索引对应的值是单调递减的。
  2. 遍历数组
    • 移除队列中不在当前窗口的索引(窗口左边界 i - k)。
    • 移除队列尾部比当前元素小的索引(保证队列单调递减)。
    • 将当前元素索引加入队列尾部。
    • 如果窗口形成(i >= k - 1),记录队列头部的最大值。
  3. 返回结果数组

时间复杂度: O(n)
空间复杂度: O(k)(队列最多存储 k 个元素)


4. Java实现

import java.util.ArrayDeque;
import java.util.Deque;

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[] result = new int[n - k + 1];
        Deque<Integer> deque = new ArrayDeque<>();

        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) {
                result[i - k + 1] = nums[deque.peekFirst()];
            }
        }

        return result;
    }
}

5. JavaScript实现

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number[]}
 */
var maxSlidingWindow = function(nums, k) {
    if (!nums.length || k <= 0) return [];

    const n = nums.length;
    const result = [];
    const deque = []; // 存储索引

    for (let i = 0; i < n; i++) {
        // 移除不在窗口内的索引
        while (deque.length > 0 && deque[0] < i - k + 1) {
            deque.shift();
        }

        // 移除队列尾部比当前元素小的索引
        while (deque.length > 0 && nums[deque[deque.length - 1]] < nums[i]) {
            deque.pop();
        }

        deque.push(i); // 加入当前索引

        // 窗口形成后,记录最大值
        if (i >= k - 1) {
            result.push(nums[deque[0]]);
        }
    }

    return result;
};

6. 复杂度分析

方法时间复杂度空间复杂度
双端队列O(n)O(k)
暴力法O(nk)O(1)

7. 总结与扩展

  • 双端队列是解决滑动窗口最大值问题的最优解,适用于大数据量。
  • 扩展题目
  • 相关数据结构
    • 双端队列(Deque)
    • 单调队列(Monotonic Queue)

🔗 相关推荐LeetCode 滑动窗口专题

如果有帮助,请点赞收藏支持! 🚀


滑动窗口最大值:Java与JavaScript的高效解法

目录

  1. 问题描述与示例
  2. 暴力法与优化思路
  3. 单调队列法详解
  4. Java代码实现
  5. JavaScript代码实现
  6. 复杂度分析
  7. 常见问题解答
  8. 总结与扩展思考

1. 问题描述与示例

题目:给定一个整数数组 nums 和一个窗口大小 k,窗口从数组最左端滑动到最右端,每次向右移动一位。返回每个窗口中的最大值。
示例
输入:nums = [1,3,-1,-3,5,3,6,7], k = 3
输出:[3,3,5,5,6,7]
解释:

  • 第一个窗口 [1,3,-1] 的最大值是 3
  • 第二个窗口 [3,-1,-3] 的最大值是 3
  • … 最后一个窗口 [3,6,7] 的最大值是 7

2. 暴力法与优化思路

暴力法

思路

  • 对每个窗口遍历 k 个元素,找到最大值。
    步骤
  1. 遍历每个起始索引 i,窗口范围为 [i, i+k-1]
  2. 在窗口内遍历元素,记录最大值。

问题

  • 时间复杂度为 O(nk),当 n=1e5 时会超时。

3. 单调队列法详解

核心思想

使用单调队列维护窗口内可能成为最大值的元素:

  • 队列中的元素按递减顺序排列,队头始终是当前窗口的最大值。
  • 队列中存储元素的索引,方便判断是否过期。

步骤

  1. 初始化双端队列:保存元素的索引。
  2. 遍历数组
    • 移除过期元素:队头索引是否已滑出窗口(<= i-k)。
    • 维护单调性:弹出队尾所有比当前元素小的索引。
    • 加入当前元素:将当前索引加入队尾。
    • 记录结果:当窗口形成后(i >=k-1),队头即为最大值。

4. Java代码实现

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.ArrayList;

public class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || nums.length == 0) return new int[0];
        Deque<Integer> deque = new ArrayDeque<>();
        List<Integer> result = new ArrayList<>();
        
        for (int i = 0; i < nums.length; i++) {
            // 移除过期的头部元素
            while (!deque.isEmpty() && deque.peek() <= i - k) {
                deque.poll();
            }
            // 维护单调性:弹出队尾所有小于当前元素的索引
            while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
                deque.pollLast();
            }
            deque.offer(i);
            // 窗口形成后记录最大值
            if (i >= k - 1) {
                result.add(nums[deque.peek()]);
            }
        }
        // 转换为数组
        int[] res = new int[result.size()];
        for (int j = 0; j < res.length; j++) {
            res[j] = result.get(j);
        }
        return res;
    }
}

关键点

  • 双端队列ArrayDeque 实现高效插入和删除。
  • 索引过期判断:队头索引是否已滑出窗口。
  • 单调性维护:确保队列始终递减。

5. JavaScript代码实现

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number[]}
 */
var maxSlidingWindow = function(nums, k) {
    const deque = []; // 存储索引
    const result = [];
    
    for (let i = 0; i < nums.length; i++) {
        // 维护单调性:弹出队尾所有小于当前元素的索引
        while (deque.length > 0 && nums[i] >= nums[deque[deque.length - 1]]) {
            deque.pop();
        }
        deque.push(i);
        // 处理过期的头部元素(在记录结果时检查)
        if (i >= k - 1) {
            // 移除过期的头部元素
            while (deque[0] <= i - k) {
                deque.shift();
            }
            result.push(nums[deque[0]]);
        }
    }
    return result;
};

关键点

  • 数组模拟双端队列:用 pushpop 操作维护队尾,用 shift 处理队头。
  • 过期检查:在记录结果时确保队头未过期。

6. 复杂度分析

时间复杂度

  • O(n):每个元素最多被加入和弹出队列一次。

空间复杂度

  • O(k):队列中最多存储 k 个元素(窗口内元素)。

7. 常见问题解答

Q1:为什么队列要保存索引而不是数值?

  • 索引可以方便判断元素是否过期(是否滑出窗口)。

Q2:为什么队列需要保持递减?

  • 递减队列确保队头始终是当前窗口的最大值。

Q3:JavaScript 的 shift() 操作会不会影响性能?

  • 每个元素最多被 shift 一次,总时间复杂度仍为 O(n)。

8. 总结与扩展思考

总结

  • 单调队列法 是解决滑动窗口问题的高效工具,时间复杂度为 O(n)。
  • 核心是通过队列的单调性快速定位最大值,并通过索引管理过期元素。

进阶思考

  • 其他窗口问题:如最小值、均值等,可类似思路优化。
  • 动态窗口问题:如最长无重复子串,可结合滑动窗口与哈希表。

推荐阅读

  • LeetCode 题目:239. 滑动窗口最大值
  • 算法学习:单调队列在动态规划中的应用(如最长递增子序列)。

通过本篇博客,你掌握了如何用单调队列高效解决滑动窗口最大值问题。无论是 Java 还是 JavaScript,核心逻辑一致,仅需调整语法细节即可。



LeetCode 239. 滑动窗口最大值:Java与JavaScript双解法详解|单调队列的精妙运用

目录

  1. 问题描述
  2. 核心思路
  3. Java解法详解
  4. JavaScript解法详解
  5. 复杂度分析
  6. 两种语言实现对比
  7. 常见错误与解决方法
  8. 举一反三练习

一、问题描述

给定一个整数数组 nums 和滑动窗口的大小 k,窗口从数组最左侧移动到最右侧,每次移动一位。要求返回每个窗口中的最大值。

示例

输入: nums = [1,3,-1,-3,5,3,6,7], k = 3
输出: [3,3,5,5,6,7]

关键要求

  • 时间复杂度必须为 O(n)
  • 数组长度可达 10^5
  • 需要处理 k=1 的特殊情况

二、核心思路:单调队列的智慧

暴力法 vs 单调队列对比

方法核心思路时间复杂度空间复杂度
暴力法每个窗口重新扫描找最大值O(nk)O(1)
单调队列动态维护递减队列获取最大值O(n)O(k)

算法步骤

  1. 维护递减队列:队列头始终是当前窗口最大值
  2. 窗口滑动操作
    • 移除窗口外元素(索引检查)
    • 维护队列递减性(弹出比新元素小的尾部元素)
  3. 记录结果:当窗口完全形成后记录队列头元素

三、Java解法详解

完整代码实现

import java.util.Deque;
import java.util.LinkedList;

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums.length == 0 || k == 0) return new int[0];
        
        Deque<Integer> deque = new LinkedList<>();
        int[] result = new int[nums.length - k + 1];
        
        for (int i = 0; i < nums.length; i++) {
            // 移除窗口外的元素
            while (!deque.isEmpty() && deque.peekFirst() < i - k + 1) {
                deque.pollFirst();
            }
            
            // 维护递减队列
            while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
                deque.pollLast();
            }
            
            deque.offerLast(i);
            
            // 记录窗口最大值
            if (i >= k - 1) {
                result[i - k + 1] = nums[deque.peekFirst()];
            }
        }
        return result;
    }
}

关键代码解析

  1. 窗口外元素检查
    deque.peekFirst() < i - k + 1  // 检查队列头是否在窗口左侧之外
    
  2. 递减性维护逻辑
    nums[i] >= nums[deque.peekLast()]  // 新元素比队尾大就弹出队尾
    
  3. 结果记录时机
    i >= k - 1  // 当窗口完全进入数组后开始记录
    

测试案例

public static void main(String[] args) {
    Solution sol = new Solution();
    int[] test1 = {1,3,-1,-3,5,3,6,7};
    System.out.println(Arrays.toString(sol.maxSlidingWindow(test1, 3)));
    // 输出 [3,3,5,5,6,7]
}

四、JavaScript解法详解

完整代码实现

const maxSlidingWindow = (nums, k) => {
    if (nums.length === 0 || k === 0) return [];
    
    const deque = [];
    const result = [];
    
    for (let i = 0; i < nums.length; i++) {
        // 移除窗口外的元素
        while (deque.length > 0 && deque[0] < i - k + 1) {
            deque.shift();
        }
        
        // 维护递减队列
        while (deque.length > 0 && nums[i] >= nums[deque[deque.length - 1]]) {
            deque.pop();
        }
        
        deque.push(i);
        
        // 记录窗口最大值
        if (i >= k - 1) {
            result.push(nums[deque[0]]);
        }
    }
    return result;
};

关键代码解析

  1. 数组模拟双端队列
    deque.shift()  // 移除队头
    deque.pop()    // 移除队尾
    deque.push()   // 加入队尾
    
  2. 索引存储技巧
    deque存储的是元素索引而非值,方便进行窗口位置检查
    

浏览器测试

console.log(maxSlidingWindow([1,3,-1,-3,5,3,6,7], 3));
// 输出 [3,3,5,5,6,7]

五、复杂度分析

时间复杂度:O(n)

  • 每个元素最多入队、出队各一次
  • 即便使用JavaScript的shift(),整体操作次数仍为O(n)

空间复杂度:O(k)

  • 队列最多存储k个元素(严格递减时)

六、Java与JavaScript实现对比

特性JavaJavaScript
队列实现标准库Deque数组模拟
元素移除效率O(1)(链表实现)O(1)平均(引擎优化)
空数组处理显式检查返回空数组同左
索引存储必须存储必须存储

七、常见错误与解决方法

错误1:直接存储数值而非索引

// 错误示例:无法进行窗口位置检查
deque.add(nums[i]);

解决方法:必须存储元素索引

错误2:忘记处理k=0的边界条件

// 错误:当k=0时会导致死循环
if (k === 0) return [];

错误3:错误计算结果索引

// 错误:结果数组索引计算错误
result[i] = ...  // 正确应为 result[i - k + 1]

八、举一反三练习

  1. 变形题剑指 Offer 59 - II. 队列的最大值(实现带max函数的队列)
  2. 进阶题862. 和至少为 K 的最短子数组(前缀和+单调队列)
  3. 系统设计:如何实时显示股票交易窗口的最高价?

学习建议
尝试使用其他数据结构(如优先队列)解决此题,比较不同方法的性能差异。


掌握单调队列的核心在于理解其动态维护最值的能力。这种数据结构在滑动窗口类问题中展现出惊人的效率,是算法工程师必须掌握的利器。下次遇到需要动态获取窗口极值的问题时,记得祭出这个法宝! 🔥



力扣239题详解:滑动窗口最大值(Java & JavaScript 双语言实现)

目录

  1. 问题描述
  2. 核心思路
  3. 解法:单调队列
  4. 复杂度分析
  5. 代码实现
  6. 测试用例
  7. 总结

1. 问题描述 {#1}

给定一个整数数组 nums 和一个整数 k,滑动窗口从数组最左侧移动到最右侧,每次窗口向右滑动一位。返回每次滑动窗口中的最大值。

示例 1

输入:nums = [1,3,-1,-3,5,3,6,7], k = 3  
输出:[3,3,5,5,6,7]  

2. 核心思路 {#2}

单调队列(双端队列):

  • 目标:维护一个队列,存储窗口内可能成为最大值的元素下标,保证队列元素严格单调递减。
  • 关键操作
    1. 移除窗口外元素:当队首元素超出窗口左边界时弹出。
    2. 维护单调性:新元素入队前,弹出队尾比它小的元素。
  • 时间复杂度:O(n),每个元素最多入队和出队一次。

3. 解法:单调队列 {#3}

Java 实现

import java.util.Deque;  
import java.util.LinkedList;  

class Solution {  
    public int[] maxSlidingWindow(int[] nums, int k) {  
        Deque<Integer> deque = new LinkedList<>();  
        int[] res = new int[nums.length - k + 1];  
        int idx = 0;  

        for (int i = 0; i < nums.length; 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[idx++] = nums[deque.peekFirst()];  
            }  
        }  
        return res;  
    }  
}  

JavaScript 实现

function maxSlidingWindow(nums, k) {  
    const deque = [];  
    const res = [];  

    for (let i = 0; i < nums.length; i++) {  
        // 移除窗口外的队首元素  
        while (deque.length > 0 && deque < i - k + 1) {  
            deque.shift();  
        }  
        // 维护单调递减性  
        while (deque.length > 0 && nums[deque[deque.length - 1]] < nums[i]) {  
            deque.pop();  
        }  
        deque.push(i);  
        // 窗口形成后记录最大值  
        if (i >= k - 1) {  
            res.push(nums[deque]);  
        }  
    }  
    return res;  
}  

4. 复杂度分析 {#4}

操作时间复杂度空间复杂度
遍历数组O(n)O(k)
Java双端队列操作 O(1)结果数组 O(n)
JavaScript数组模拟队列 O(n)结果数组 O(n)

5. 测试用例 {#6}

Java 测试

public static void main(String[] args) {  
    Solution solution = new Solution();  
    int[] nums = {1,3,-1,-3,5,3,6,7};  
    int[] result = solution.maxSlidingWindow(nums, 3);  
    System.out.println(Arrays.toString(result)); // [3,3,5,5,6,7]  
}  

JavaScript 测试

console.log(maxSlidingWindow([1,3,-1,-3,5,3,6,7], 3)); // [3,3,5,5,6,7]  

7. 总结 {#7}

  • 单调队列:通过动态维护递减队列,高效获取窗口最大值,时间复杂度优化至 O(n)。
  • 语言差异:Java 使用 Deque 双端队列,JavaScript 需用数组手动维护(可优化为指针避免 shift 的低效操作)。
  • 适用场景:实时获取动态窗口内的极值,如股票分析、实时监控等高频数据处理场景。

参考资料
单调队列原理与实现逻辑
双端队列操作与复杂度分析


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南北极之间

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值