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

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

文章目录

🔗 查看完整专栏(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 个元素的索引。
相关推荐
Raink老师13 小时前
【AI面试临阵磨枪-70】Agent 系统如何做分布式调度、跨服务协作、故障恢复?
人工智能·面试·职场和发展
lishi_199113 小时前
一键部署MoviePilotV2实现NAS全自动追剧
python·媒体·moviepilot
tedcloud12313 小时前
RTK部署教程:构建稳定的AI Workflow环境
服务器·javascript·人工智能·typescript·ocr
Raink老师13 小时前
【AI面试临阵磨枪-71】如何用 AI 优化推荐系统、内容审核、广告创意、搜索体验?
人工智能·面试·职场和发展
AI医影跨模态组学13 小时前
Biomarker Res(IF=11.5)安徽医科大学第一医院:基于机器学习的放射组学模型:子宫内膜癌患者的预后预测及机制探索
人工智能·深度学习·论文·医学·医学影像·影像组学
ftpeak13 小时前
Mooncake:以 KVCache 为中心的分离式 LLM 服务架构
人工智能·ai·架构·ai编程·ai开发
Terrence Shen13 小时前
Hermes agent的tools是怎么落地应用的系列
人工智能·llm·agent·hermes
EllinY14 小时前
CF2217E Definitely Larger 题解
c++·笔记·算法·构造
Raink老师14 小时前
【AI面试临阵磨枪-72】电商全场景 AI Agent 设计(商品咨询 / 订单 / 物流 / 售后 / 退款)
人工智能·面试·职场和发展
仙女修炼史14 小时前
CNN更看重Texture还是shape:imagenet-trained cnns are biased
论文阅读·人工智能·cnn