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

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

文章目录

🔗 查看完整专栏(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 个元素的索引。
相关推荐
sell-cloud3 小时前
Prompt Engineering要凉了?Fable 5.5能“猜“意图,但银行正在封杀AI Agent——企业AI的真正护城河是什么
人工智能·prompt
Navicat中国4 小时前
Navicat 18 内置 AI 助手让数据库开发从繁琐操作迈向智能协作
大数据·人工智能·数据库开发
4SAPI4 小时前
多模型请求路由怎么设计?成本、速度和能力如何平衡
人工智能·python·gateway·php
魔乐社区5 小时前
翼芯・智算测试与适配优化专区,让国产算力“选得准、跑得通、用得好“
人工智能·开源·大模型
XCDoris7 小时前
郴州logo设计机构
大数据·人工智能·python
Goboy7 小时前
一个人带三个Agent,我用全新TRAE从0到1搭了个资讯周刊站
人工智能·trae
俊哥V7 小时前
每日 AI 研究简报 · 2026-10-19
人工智能·ai
AI_AGENT_DEV_AI7 小时前
AI英语教培智能体的开发
人工智能
物联网软硬件开发-轨物科技7 小时前
【技术白皮书】光伏清检一体化机器人系统解决方案技术白皮书
大数据·运维·网络·人工智能·机器人
gCode Teacher 格码致知7 小时前
Pandas教学-17:Series与Dataframe的关系
开发语言·python·pandas