队列与单调队列基础原理与题目说明

队列与单调队列基础原理与题目说明

文章目录

🔗 查看完整专栏(LeetCode基础算法专栏

点击阅读:Python 数据结构与语法速查笔记

点击阅读:哈希表基础原理与题目说明

点击阅读:双指针基础原理与题目说明

点击阅读:滑动窗口基础原理与题目说明

特别说明:

本文为个人的 LeetCode 刷题与学习笔记,内容仅供学习与交流使用,禁止转载或用于商业用途。需要强调的是,文中的题目解法不一定是最优解(可能存在时间或空间复杂度的进一步优化空间),主要目的是分享个人的解题思路与逻辑实现,仅供参考。 笔记内容为个人理解与总结,可能存在疏漏或偏差,欢迎读者自行甄别并交流探讨。

一、 队列(Queue)与双端队列(Deque)核心机制

1.1 基础概念

  • 普通队列(Queue) :遵循 先进先出(FIFO, First In First Out) 原则的线性数据结构。常用于广度优先搜索(BFS)、任务调度等场景。
  • 双端队列(Deque) :允许在两端(队首和队尾)同时进行插入和删除操作 的高阶结构。在 Python 中通常使用 collections.deque 实现。

1.2 核心操作与优势

在 Python 中,双端队列的左右两端操作时间复杂度均为 O ( 1 ) O(1) O(1):

  • 入队append(x)(尾部插入) / appendleft(x)(头部插入)
  • 出队pop()(尾部弹出) / popleft()(头部弹出)

算法优势

双端队列能够高效地维护一个区间(窗口)的元素集合。因为可以同时从队首和队尾进行动态调整,它成为了解决滑动窗口极值问题的绝佳容器。

二、 核心对比:单调队列 vs 单调栈

在算法实战中,单调队列单调栈常常容易混淆。单调队列本质上是基于双端队列实现的,用于维护区间内元素的单调性(递增或递减)。两者的核心区别如下:

特性维度 单调队列(Monotonic Queue) 单调栈(Monotonic Stack)
访问范围 可在 队首队尾 两端操作 仅能在 栈顶 一端操作
典型用途 滑动窗口极值(如窗口最大/最小值) 全局次序问题(如寻找下一个更大元素、括号匹配)
维护规律 队列中元素严格单调递减或递增 栈中元素单调递增或递减
动态维护 可以随窗口滑动,从队首主动删除过期元素 属于全局累加问题,通常不涉及因"过期"而强制弹出

三、 单调队列实战演练

单调队列最经典的实战场景即是"滑动窗口最大值",它将暴力解法的 O ( n ⋅ k ) O(n \cdot k) O(n⋅k) 复杂度完美优化到了 O ( n ) O(n) O(n)。

239. 滑动窗口最大值

题目描述

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

解题思路

使用双端队列(deque)维护一个单调递减 的队列,队列中存储的是数组的索引而非具体值(方便判断元素是否滑出窗口)。

  • 队首:永远维护当前窗口内最大值的索引。
  • 队尾:当新元素准备入队时,将队列中所有对应值小于当前新元素的索引全部弹出(因为当前新元素不仅更大,且生命周期更长,前面的小元素永无出头之日)。

状态流转模拟(单调队列)

以数组 [1, 3, -1, -3, 5, 3, 6, 7]k=3 为例:

窗口状态 队列内索引 队列对应元素 状态流转解释
[1, 3, -1] [1, 2] [3, -1] 入队 3 时,因 3 > 1,将 1 的索引弹出(1 不可能成为后续最大值)
[3, -1, -3] [1, 2, 3] [3, -1, -3] 入队 -3,因 -3 < -1,符合单调递减,保留,队列长度为 3
[-1, -3, 5] [4] [5] 入队 5,因 5 大于队尾对应值,队尾全部弹出 ,最后只保留 5
[-3, 5, 3] [4, 5] [5, 3] 入队 3,因 3 < 5,符合递减,保留入队
[5, 3, 6] [6] [6] 入队 6,因 6 大于前面的值,队尾全部弹出
[3, 6, 7] [7] [7] 入队 7,因 7 > 6,队尾弹出,此时队首为 7

核心代码

py 复制代码
from typing import List
from collections import deque

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        # 1. 创建双端队列,用于存储元素的索引
        deq = deque()
        ans = []

        # 2. 遍历数组,动态维护单调队列
        for i, num in enumerate(nums):
            # [Process] 维护单调性:若队尾对应的元素小于当前元素,则弹出队尾
            # 因为当前元素更大且更晚过期,被弹出的元素绝对不可能成为最大值
            while deq and nums[deq[-1]] < num:
                deq.pop()
                
            # [Process] 将当前元素的索引加入队尾
            deq.append(i)
            
            # [Process] 维护窗口边界:若队首的索引已经滑出当前窗口(i-k),则弹出队首
            if deq[0] <= i - k:
                deq.popleft()
                
            # [Process] 记录答案:当窗口形成后(索引 i >= k-1),队首即为当前窗口最大值
            if i >= k - 1:
                ans.append(nums[deq[0]])
                
        return ans

复杂度分析

  • 时间复杂度 : O ( n ) O(n) O(n)。虽然 for 循环内嵌套了 while 循环,但每个元素的索引最多入队一次、出队一次,总体操作次数不超过 2 n 2n 2n。
  • 空间复杂度 : O ( k ) O(k) O(k)。单调队列中最多存储 k k k 个元素的索引。
相关推荐
苏三的开发日记10 分钟前
Embedding与向量数据库
人工智能
看月亮的方源12 分钟前
Transformer原理讲解
人工智能
码流怪侠14 分钟前
【GitHub】TextGen:开源本地大模型运行平台的终极解决方案
python·程序员·github
peterfei16 分钟前
IfAI v0.4.6 发布:多线程并发对话 + Rust TUI 架构重构实战
人工智能·ai编程
疯狂成瘾者17 分钟前
总价包干(Lump Sum / Fixed Price Contract)
人工智能
智枢圈18 分钟前
[理论篇-11]AI Agent(智能体)——不只是会答话的AI,而是会干活的AI
人工智能
薛定猫AI23 分钟前
【深度解析】Google AI Studio Vibe Coding 更新:从 Prompt 生成到可视化应用构建闭环
人工智能·prompt
2301_7820404523 分钟前
JavaScript中Map在频繁增删键值对场景下的稳定性
jvm·数据库·python
小雨青年25 分钟前
GitHub Copilot Commit Message 生成与自定义配置优化指南
人工智能·github·copilot
俊哥V25 分钟前
AI一周事件 · 2026-04-29 至 2026-05-05
人工智能·ai