[路飞]_单调队列及算法题应用

359 阅读2分钟

「这是我参与2022首次更文挑战的第14天,活动详情查看:2022首次更文挑战」。

单调队列

首先单调队列也是队列,是一种维护单调性的队列。单调性分为单调递增和单调递减,因此单调队列也分为单调递增队列和单调递减队列

  • 单调递增队列:保证队列头元素一定是当前队列的最小值,用于维护区间的最小值
  • 单调递减队列:保证队列头元素一定是当前队列的最大值,用于维护区间的最大值

单调队列适合解决 RMQ(Range Minimum/Maximum Query)问题,即区间最值查询

  • 入队操作:队尾入队,会把之前破坏单调性的元素都从队尾移出(维护单调性)
  • 出队操作:如果队首元素超出区间范围,将元素从队首出队
  • 元素性质:队首元素,永远是当前维护区间的(最大/最小)值

动画演示:单调队列维护滑动窗口最小值 滑动窗口最小值.gif

力扣算法题

239. 滑动窗口最大值

image.png

分析

  • 可以用一个单调递减队列维护区间内的最大值
  • 每次滑动窗口,需要将当前元素从队尾入队,在入队前,需要判断当前队列的队尾元素是否小于当前需要入队的元素,如果小于当前元素(破坏了单调递减的性质)就将该元素从队尾出队,直到没有小于当前元素,然后将当前元素入队
  • 如果队首元素不在区间内需要从队首出队
  • 此时的队首元素就是当前区间的最大值

代码实现

/**
 * @param {number[]} nums
 * @param {number} k
 * @return {number[]}
 */
var maxSlidingWindow = function(nums, k) {
  var queue = [], ans = []
  var n = nums.length

  for(var i = 0; i < n; i++) {
    var cur = nums[i]
    // 将队尾小于需要入队的元素出队,维护单调性
    while(queue.length && nums[queue[queue.length - 1]] <= cur) {
      queue.pop()
    }
    // 将当前元素的索引入队
    queue.push(i)
    // 如果队首元素已经不在活动窗口中则出队
    while(i - queue[0] >= k) {
      queue.shift()
    }
    // 当窗口中的元素有K个时输出结果
    if(i >= k - 1) ans.push(nums[queue[0]])
  }

  return ans
};