Python数据结构——循环队列

一句话:循环队列是用固定长度数组模拟环形、靠取模绕回解决「假溢出」的队列。物理上还是一段连续数组,逻辑上首尾相接成了环。

一、为什么需要循环队列

普通队列用数组实现时,出队只动 front 指针,元素不断从队头离开,前面就空出一堆位置。但 rear 指针一路向右,等到它撞到数组末尾时,哪怕前面空着,也插不进新元素了------这就是假溢出(false overflow)

现象 真相
数组前面明明有空位,enqueue 却报「满」 rear 到了物理末尾,没绕回去
反复出队后空间「看似」浪费 front 只增不减,没有回收

循环队列的解法:把数组首尾相接想成环rear/front 走到末尾时取模绕回开头。这样「前面空出来的位置」能被重新利用,假溢出消失。

二、核心思路:两个指针 + 取模

只靠 frontrear 两个下标不够------当 front == rear 时,你分不清是「空」还是「满」。两种解法:

  1. 加一个 size 计数器(推荐,直观,不浪费空间)。
  2. 牺牲一个空位:让 (rear + 1) % capacity == front 表示满(容量为 N 只能装 N-1)。

指针前进都靠取模,这是「循环」二字的核心:

python 复制代码
rear = (rear + 1) % capacity   # 入队后前进,到末尾自动绕回开头
front = (front + 1) % capacity # 出队同理

三、完整实现(带 size 计数器,推荐版)

# D:\博客\circular_queue.py(节选:CircularQueue 类)

python 复制代码
# D:\博客\circular_queue.py
class CircularQueue:
    def __init__(self, capacity: int):
        if capacity <= 0:
            raise ValueError("容量必须大于 0")
        self.capacity = capacity
        self.queue = [None] * capacity
        self.front = 0   # 队头下标(出队位置)
        self.rear = 0    # 队尾下标(下一个入队位置)
        self.size = 0    # 当前元素个数

    def is_empty(self) -> bool:
        return self.size == 0

    def is_full(self) -> bool:
        return self.size == self.capacity

    def enqueue(self, item) -> None:
        if self.is_full():
            raise OverflowError("队列已满,无法入队")
        self.queue[self.rear] = item
        self.rear = (self.rear + 1) % self.capacity
        self.size += 1

    def dequeue(self):
        if self.is_empty():
            raise IndexError("队列为空,无法出队")
        item = self.queue[self.front]
        self.queue[self.front] = None
        self.front = (self.front + 1) % self.capacity
        self.size -= 1
        return item

    def peek(self):
        if self.is_empty():
            return None
        return self.queue[self.front]

    def __len__(self):
        return self.size

    def __repr__(self):
        items = []
        i = self.front
        for _ in range(self.size):
            items.append(self.queue[i])
            i = (i + 1) % self.capacity
        return f"CircularQueue({items})"

__repr__ 里用 i = (i + 1) % capacity 按逻辑顺序遍历,保证打印结果和「入队顺序」一致,调试时一眼能看懂。

小结:enqueue 放 rear、动 rear;dequeue 取 front、动 front;两者都取模绕回。判空判满交给 size,干净无歧义。

四、另一种写法:牺牲一个空位判满

不想维护 size 时,用「永远留一个空槽」来区分空和满:

python 复制代码
class CircularQueueWasteOne:
    def __init__(self, capacity: int):
        self.capacity = capacity
        self.queue = [None] * capacity
        self.front = 0
        self.rear = 0

    def is_empty(self) -> bool:
        return self.front == self.rear

    def is_full(self) -> bool:
        return (self.rear + 1) % self.capacity == self.front

    def enqueue(self, item) -> None:
        if self.is_full():
            raise OverflowError("队列已满,无法入队")
        self.queue[self.rear] = item
        self.rear = (self.rear + 1) % self.capacity

    def dequeue(self):
        if self.is_empty():
            raise IndexError("队列为空,无法出队")
        item = self.queue[self.front]
        self.queue[self.front] = None
        self.front = (self.front + 1) % self.capacity
        return item

代价:N 容量的数组只能装 N-1 个元素。新手直接用第三节的 size 版,少踩坑。

五、和 collections.deque 的关系

Python 标准库的 deque(双端队列)底层就是类似环形缓冲,两端增删都是 O(1)。deque(maxlen=N) 更是开箱即用的有界循环队列 ------满了再 append 自动丢掉最老的那头,正好对应我们手写的「循环覆盖」语义。

python 复制代码
# D:\博客\circular_queue.py
from collections import deque
dq = deque(maxlen=3)   # maxlen 满了自动丢最老的,自带「循环」语义
for x in range(5):
    dq.append(x)
print(dq)              # deque([2, 3, 4], maxlen=3)

deque 常用 API:

方法 作用
append(x) / appendleft(x) 右端 / 左端入队
pop() / popleft() 右端 / 左端出队
rotate(n) 整体循环平移 n 位
maxlen 构造参数,定长环形

六、什么时候用

  • 手写循环队列:面试手写、理解底层、或特定嵌入式/缓冲场景需要完全掌控。
  • 直接 deque:工程里 99% 的情况。滑动窗口、最近 N 条记录、BFS、栈/队列通吃。
  • 一句话 :凡是频繁从一头或两头增删,别用 list(头部操作 O(n)),用 deque;要练手或面试才手写循环队列。

总结:循环队列 = 数组 + front/rear 取模绕回 + size 判空满,专治普通队列的假溢出。真实项目里 collections.deque(maxlen=N) 一句话顶替它的全部功能。手写一遍是为了把「为什么这么设计」刻进脑子,不是让你上线造轮子。

相关推荐
码云骑士7 分钟前
120-视频理解-大模型视频分析-抽帧-自动字幕摘要-高光片段
python·音视频
2601_9499506311 分钟前
在线刷题用什么小程序?认识一下能导资料、AI出题的练题簿
人工智能·学习·小程序·刷题·小程序推荐
xfan_me13 分钟前
手机在网状态接口-空号查询-空号过滤API
数据库·人工智能·python·智能手机
HugoStudio_SWAN15 分钟前
【按键反馈】C++ 实现键盘烟花秀
开发语言·c++·学习·程序人生
测试秃头怪16 分钟前
Postman中变量的使用
自动化测试·软件测试·python·测试工具·测试用例·接口测试·postman
ynchyong17 分钟前
词云(Word Cloud) 使用方法
python·统计·词云
艾莉丝努力练剑26 分钟前
【AI大模型接入SDK】DeepSeek API 基础概述
c++·人工智能·学习·ai·面试·deepseek
知识分享小能手27 分钟前
概理论与数理统计学习教程,从入门到精通,马尔可夫链(25)
学习·机器学习·概率论
Alkaid207727 分钟前
我把文件和脚本放一起了,Python 就是看不见?新手必看的绝对路径 vs 相对路径终极避坑指南
python·ai编程
夏炳辉.35 分钟前
零基础Linux PostgreSQL(psql)从安装到集群高可用全套学习大纲
linux·学习·postgresql