面试题:滑动窗口最大值

336 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

这里是人不狠话也不多的我,相遇即是缘,面试遇见这类题怎么解决?直接进入正题

这是一道LeetCode原题,原题参考 leetcode.cn/problems/sl…

输入数组:nums

输入窗口大小:k

要求:返回滑动的窗口中的最大值

示例:

nums = [ 1 , 4 , 1 , 5 , 2 , 6 ]                k = 3

滑动窗口过程:

[1,4,1] => [4,1,5] => [1,5,2] => [5,2,6]
  4           5          5          6

返回 [ 4 , 5 , 5 , 6 ]

优先队列:对于这类求最大值的问题,我们很容易想到优先队列,不明白优先队列是什么的小伙伴也不用担心,这里用简单的文字说明

在本题中,优先队列就好像,一个战斗力高的人,一路上清除战斗力低于自己的人,最终登上巅峰宝座(queue[0] )更是带着一群战斗力不如自己的小弟( queue[1]....queue[n] )。但是最是巅峰留不住,随着时间的流逝( 窗口的滑动 ),宝座上的人要么被时间淘汰;要么就是长江后浪推前浪,终是廉颇老矣,尚能饭否?总会有一个战斗力更高的人将上一任宝座的主人拉下宝座,周而复始。

本题思路:我们创建一个双向队列存储元素下标。保持其长度在 [ 0 , k ]之间,队列单调递减,如果右窗口元素大于队列尾部元素,则依次删除队列尾部小于右窗口的元素,直至队列为空。如果右边窗口元素小于队列尾部元素,则直接加入队列,并且判断队列长度以及队列头部元素是否小于左窗口。

代码

var maxSlidingWindow = function (nums, k) {

    //创建一个优先队列,存储元素下标(方便后续下标与左窗口做比较)
    let queue = []   
    let res = []
    for (let r = 0; r < nums.length; r++) {
    
        //循环判断,如果右窗口的数字大于优先队列的最小值,则从队尾删除队列元素
        while (queue.length && nums[r] >= nums[queue[queue.length - 1]]) {
            queue.pop()
        }
        
        //将队列中小于右窗口的元素下标清除干净,将r 加入队列
        queue.push(r)
        
        //判断队列头部是否在窗口内,如果在窗口外,则从队列头部删除出队,
        let l = r - k + 1
        if (queue[0] < l) {
            queue.shift()
        }
        
        当窗口大小为k时,则每一次窗口移动,都会向结果中加入窗口最大值,最大值即是优先队列的queue[0]作为下标在nums中的值
        if (r + 1 >= k) {
            res.push(nums[queue[0]])
        }
    }
    return res
};