Skip to content

316

约 812 字大约 3 分钟

日报leetcode

2026-03-16

  • 重新理解了 3月13 没理解透的东西
  • 完成了一道 hard
  • 开始刷 Java 1000题,计划一天 30 道左右
  • 实在是不想再看算法了,今天多看会业务

介绍

单调队列** 一种基于 双端队列 的数据结构,用于在滑动窗口或区间问题中高效维护最大值或最小值 它的核心思想是:

  • 队列中的元素保持单调递增单调递减
  • 插入新元素时,从队尾移除不满足单调性的元素;
  • 移动窗口时,从队首移除超出范围的元素 这种结构能保证每个元素仅进出队一次,因此时间复杂度为 O(n)

题例

滑动窗口最大值

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

返回 滑动窗口中的最大值

思路解析

在这里,我们要维护一个从大到小的单调队列

代码实现

手写 Java 数组(更快

class Solution {  
    public int[] maxSlidingWindow(int[] nums, int k) {  
        int n = nums.length;  
        int[] ans = new int[n - k + 1];  // 窗口个数
        int[] q = new int[n];            // 手写队列,存入原数组的下标
        int head = 0, tail = -1;  
  
        for (int i = 0; i < n; i++) {    
            while (head <= tail && nums[q[tail]] <= nums[i]) {  
                tail--;                  // 右边出队
            }  
            q[++tail] = i;               // 右边入队
  
            int left = i - k + 1;  
            if (q[head] < left) {        // 队首离开窗口
                head++;  
            }  
            if (left >= 0) {             // 在窗口端记录答案
                ans[left] = nums[q[head]];  // 窗口最大值就在队首
            }  
        }  
        return ans;  
    }  
}

使用Deque(双端队列)以此来维护队列比较方便且好理解了,但是运行速度会慢很多 原理和上一个是一样的,但是更方便理解

首先复习一下双端队列的相关用法

操作方向动作分类抛异常的方法不抛异常的方法 (推荐)实际功能描述
队首 (First/左端)插入addFirst(e)offerFirst(e)将元素 e 塞入队列最左侧。
删除removeFirst()pollFirst()将队列最左侧的元素移除,并返回它的值。
查看getFirst()peekFirst()仅查看最左侧的元素是什么,不移除
队尾 (Last/右端)插入addLast(e)offerLast(e)将元素 e 塞入队列最右侧。
删除removeLast()pollLast()将队列最右侧的元素移除,并返回它的值。
查看getLast()peekLast()仅查看最右侧的元素是什么,不移除
代码实现
class Solution {  
    public int[] maxSlidingWindow(int[] nums, int k) {  
        int n = nums.length;  
        int[] ans = new int[n - k + 1];  
        Deque<Integer> q = new ArrayDeque<>();  
  
        for (int i = 0; i < n; i++) {  
        // 比较队列队尾值与即将入队值的大小 
            while (!q.isEmpty() && nums[q.getLast()] <= nums[i]) {  
        // 维护单调性
                q.removeLast();  
            }  
        // 将下标值添加至队尾
            q.addLast(i);  
		// 计算窗口左边界
            int left = i - k + 1;  
        // 如果最左侧元素小于左窗口边界
            if (q.getFirst() < left) {  
        // 直接移除队首元素
                q.removeFirst();  
            }  
		// 条件成立意味着窗口的成型
            if (left >= 0) {  
        // 将最大值添加至答案中
                ans[left] = nums[q.getFirst()];  
            }  
        }  
        return ans;  
    }  
}