Python数据结构——循环队列

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

一、为什么需要循环队列

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

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

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

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

只靠 front、rear 两个下标不够------当 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) 一句话顶替它的全部功能。手写一遍是为了把「为什么这么设计」刻进脑子,不是让你上线造轮子。

相关推荐
敬往事一杯酒哈5 小时前
海康 AGV 导航读码器学习
学习
qq_284274057 小时前
机械原理笔记:平面机构自由度计算(复合铰链、局部自由度、虚约束)与四杆机构入门(含考点)
笔记·学习·平面·自动化·制造
全栈练习生7 小时前
AI Agent 沙箱
python·ai
qq_284274058 小时前
数控铣床与加工中心笔记:铣刀与刀柄系统、G54试切对刀、镜像加工指令、孔加工工艺(钻扩铰镗)与固定循环G73/G83/G81/G76/G87/G84
笔记·学习·自动化·制造
SEO_juper8 小时前
用 Python 写一个 GEO 可见性检查脚本:你的网站现在能被 AI 引用吗
开发语言·人工智能·爬虫·python·seo·外贸独立站
Titan20249 小时前
MySQL访问个人学习笔记
笔记·学习·mysql
用户8356290780519 小时前
使用 Python 对 Excel 工作表进行排序
后端·python
for_ever_love__10 小时前
MySQL 全文索引实战:FULLTEXT、ngram 中文分词与 MATCH AGAINST 到底该怎么用
java·python·mysql·全文检索·分词·索引·ngram
用户83562907805110 小时前
使用 Python 合并 PowerPoint 演示文稿
后端·python
小雪崩10 小时前
嵌入式学习 day64:字符设备驱动
学习