单调队列应用详细讲解(leetcode239. 滑动窗口最大值)

文章介绍了如何利用单调队列这一数据结构解决LeetCode上的滑动窗口最大值问题。单调队列可以在O(1)的时间复杂度内进行插入、删除和获取最大值操作,特别适合处理滑动窗口问题。文章给出了具体的Java实现,包括单调队列的push、pop和getMax方法,以及如何结合滑动窗口移动更新队列。
摘要由CSDN通过智能技术生成

单调队列

对应leetcoe题目 239. 滑动窗口最大值

其实在JDK API中是没有实现单调队列这个东西的,但是单调队列的特性可以帮助我们很好的解决这道题目。

定义

单调队列是一种特殊的队列,它可以在 O(1) 的时间内实现以下操作:

  1. 在队列尾部添加一个元素;

  2. 删除队列头部的元素;

  3. 获取队列中的最大值或最小值。

单调队列通常用于解决一些滑动窗口问题,例如在一个数组中,找到每个长度为 k 的子数组中的最大值或最小值。(完美适用本题)

单调队列的实现比较简单,通常使用双端队列来实现。具体来说,可以使用一个双端队列 deque 来存储数组中的元素,deque 中的元素按照单调递增(单调递减)的顺序排列,队头为最大值(最小值)。

  • 当添加一个新元素时,首先将队列中比它小的元素都删除,保持队列的单调性;然后将新元素加入队列尾部。

  • 当删除一个元素时,判断队头元素是否等于被删除的元素,如果是则将其从队列头部删除。

这样,单调队列可以保持队列的单调性,并在 O(1) 的时间内实现插入、删除和获取最大值或最小值的操作。

例如:

nums = [1,3,-1,-3,5,3,6,7], k = 3(滑动窗口大小为3)

因为我们要找窗口内的最大值,所以要将最大值放在队头的位置,故队列为单调递减队列

  1. 滑动窗口在开始位置时:先将前三个数入队

    • 1入队:que = 1

    • 3入队:que = 3(因为是单调递减队列,所以会将队尾所有小于3的值都从队尾弹出

    • -1入队:que = 3,-1

  2. 将前三个的最大值(队头元素,放入结果集):res[idx] = que.pollFirst();

  3. 开始移动滑动窗口

    • 窗口左侧(nums[0]=1)右移一位:判断nums[0]和当前队头元素(3)是否相等。相等则需要队头元素出队,然后窗口左侧右移;如果不相等,窗口左侧直接右移

    • 窗口右侧(nums[k-1]=-1)右移一位:循环判断nums[k]是否大于队列队尾元素,大于则队尾元素弹出,直到不大于为止。再把nums[k]入队。

    • 此时的滑动窗口为:3,-1,-3。队列里的值为:3,-1,-3.重复此动作,一直到遍历完整个数组。

代码

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int len = nums.length;
        //存放结果
        int[] res = new int[len-k+1];
        //把双端队列当成消息队列用,改造一下
        MyQueue que  = new MyQueue();
        //存res的下标
        int idx = 0;
        //先将前k个元素入队
        for (int i = 0; i < k; i++) {
            que.push(nums[i]);
        }
        res[idx++] = que.getMax();
        for (int i = k; i < len; i++) {
            int right = nums[i];
            int left = nums[i-k];
            //滑动窗口左侧弹出数据
            que.pop(left);
            //滑动窗口右侧压入数据
            que.push(right);
            //找出窗口内最大值进入res
            res[idx++] = que.getMax();
        }
        return res;
    }
}
//定义单调队列
class MyQueue{
    Deque<Integer> que = new LinkedList<>();
    //如果当前队尾元素小于要push的元素,则与要弹栈
    public void push(int val){
        while (!que.isEmpty() && que.peekLast() < val) {
            que.pollLast();
        }
        que.offer(val);
    }
    //每次弹出的时候要比较弹出的值和队列出口的值是否相等
    public void pop(int val){
        if(!que.isEmpty() && que.peekFirst() == val) {
            que.pollFirst();
        }
    }
    public int getMax(){
        return que.peekFirst();
    }
}

参考资料:

代码随想录 (programmercarl.com)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值