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

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

文章目录

🔗 查看完整专栏(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 个元素的索引。
相关推荐
企微增长观察2 小时前
企业微信怎么注册?2026年完整注册流程
java·人工智能·企业微信
东离与糖宝2 小时前
Python 生成器基础:yield 核心原理
人工智能
亚空间仓鼠2 小时前
Python学习日志(四):实例
开发语言·python·学习
AI2512242 小时前
AI文生视频技术解析:主流工具的模型架构与能力对比
人工智能·架构·音视频
pen-ai2 小时前
IRLS(迭代加权最小二乘)详解:基于 Huber Loss 的鲁棒回归
人工智能·数据挖掘·回归
阿杰学AI2 小时前
AI核心知识122—大语言模型之 直接偏好优化(简洁且通俗易懂版)
人工智能·算法·机器学习·ai·强化学习·dpo·直接优化偏好
克里斯蒂亚诺·罗纳尔达2 小时前
智能体学习22——智能体间通信(A2A)
人工智能·学习·ai
算力百科小星2 小时前
Web3.0节点部署专用:双卡GPU算力租用,延迟≤4ms
人工智能·图像渲染·智星云
东离与糖宝2 小时前
计算机网络五层模型:基础架构一次讲清
人工智能