leetcode:239.滑动窗口最大值(JavaScript)

本文介绍了如何使用单调队列解决滑动窗口最大值的问题,避免了原始解决方案中对窗口内元素排序导致的时间复杂度过高。通过维持一个单调递减的队列,可以在O(n)的时间复杂度内找到每个窗口的最大值。文中还提供了详细的代码实现和解释,包括队列的更新逻辑,以及如何保证队列长度和正确记录最大值。
239. 滑动窗口最大值

给你一个整数数组 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]

提示:

  • 1 <= nums.length <= 105
  • -104 <= nums[i] <= 104
  • 1 <= k <= nums.length

最初的想法是模拟窗口向右移,每次对窗口内元素进行排序,取最大的数。超时了。

var maxSlidingWindow = function(nums, k) {
    let res = [], que = [];
    for (let i = 0; i <= nums.length; i++) {
        if (i < k) {
            // 维持窗口长度为k
            que.push(nums[i]);
        } else {
            // 深拷贝
            let tem = [...que];
            // 排序
            tem.sort((a, b) => a - b);
            // 记录将当前窗口中最大值
            res.push(tem[k - 1]);
            // 删除当前窗口最左边的数
            que.shift();
            // 当前窗口向右移一位
            que.push(nums[i]);
        }
    }
    return res;
};

后边看题解后,可以用单调队列(单调队列:从头到尾单调递增或递减)做,维持一个单调递减的队列,队头总是当前窗口最大的值。

对于如何维持一个单调队列,代码中有详细注释,下面模拟过程

单调队列示例:[8, 5, 2, 1]
维护过程:[8, 5, 2, 1], 要添加的新元素 4
第一次:4 > 1, 删除队尾元素删除,操作后队列:[8, 5, 2]
第二次:4 > 2, 删除队尾元素删除,操作后队列:[8, 5]
第三次:4 < 5, 将4添加到队尾,操作后队列:[8, 5, 4];

你会发现队列的长度并不是一定总是等于k,但是注意

  // 如果对头元素时上一个窗口值得开头就弹出,更新对列头
if (que[0] === nums[start++])
  	que.shift();

这一段代码判断如何更新队头值得好好想一想,它可以保证既是队列长度小于等于k,也能取得正确的数存入结果数组。

完整代码

var maxSlidingWindow = function (nums, k) {
  let res = [], que = [], start = 0, i = 0;
  while (i < k) {
      // 初始化长度为k的窗口
    add(que, nums[i++]);
  }
  while (i <= nums.length) {
      // 记录当前窗口最大值
    res.push(que[0]);
      // 更新当前窗口
    add(que, nums[i]);
      // 如果对头元素时上一个窗口值得开头就弹出,更新对列头
    if (que[0] === nums[start++])
      que.shift();
    i++;
  }
  return res;
};
// 维持单调队列
function add(que, value) {
    // 将要加入的新元素从队尾开始与元素比较
    let back = que[que.length - 1];
    // 若队尾元素比当前元素小就将队尾元素从队列中删除
    while (back !== undefined && back < value) {
         // 更新队尾元素
      que.pop();
      back = que[que.length - 1];
    }
    // 将新元素加入队列
    que.push(value);
}

如果还有小伙伴不懂可以去看看代码随想录,那里有更加详细的解题过程哦!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值